home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir37 / ms_sh23s.zip / SRC / SH8.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  77KB  |  3,884 lines

  1. /*
  2.  * MS-DOS SHELL - Unix File I/O Emulation
  3.  *
  4.  * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited
  5.  *
  6.  * This code is subject to the following copyright restrictions:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  * The directory functions opendir, readdir, closedir are based on the public
  17.  * domain implementation for MS-DOS written by Michael Rendell
  18.  * ({uunet,utai}michael@garfield), August 1897
  19.  *
  20.  * startup () is based on EMX/GCC startup.c in emx/lib/src/startup.  Copyright
  21.  * (c) 1990-1993 by Eberhard Mattes.
  22.  *
  23.  *    $Header: /usr/users/istewart/shell/sh2.3/Release/RCS/sh8.c,v 2.16 1994/08/25 20:49:11 istewart Exp $
  24.  *
  25.  *    $Log: sh8.c,v $
  26.  *    Revision 2.16  1994/08/25  20:49:11  istewart
  27.  *    MS Shell 2.3 Release
  28.  *
  29.  *    Revision 2.15  1994/02/23  09:23:38  istewart
  30.  *    Beta 233 updates
  31.  *
  32.  *    Revision 2.14  1994/02/01  10:25:20  istewart
  33.  *    Release 2.3 Beta 2, including first NT port
  34.  *
  35.  *    Revision 2.13  1994/01/20  14:51:43  istewart
  36.  *    Release 2.3 Beta 1
  37.  *
  38.  *    Revision 2.12  1994/01/11  17:55:25  istewart
  39.  *    Release 2.3 Beta 0 patches
  40.  *
  41.  *    Revision 2.11  1993/11/09  10:39:49  istewart
  42.  *    Beta 226 checking
  43.  *
  44.  *    Revision 2.10  1993/08/25  16:03:57  istewart
  45.  *    Beta 225 - see Notes file
  46.  *
  47.  *    Revision 2.9  1993/07/02  10:21:35  istewart
  48.  *    224 Beta fixes
  49.  *
  50.  *    Revision 2.8  1993/06/14  11:00:12  istewart
  51.  *    More changes for 223 beta
  52.  *
  53.  *    Revision 2.7  1993/06/02  09:52:35  istewart
  54.  *    Beta 223 Updates - see Notes file
  55.  *
  56.  *    Revision 2.6  1993/02/16  16:03:15  istewart
  57.  *    Beta 2.22 Release
  58.  *
  59.  *    Revision 2.5  1993/01/26  18:35:09  istewart
  60.  *    Release 2.2 beta 0
  61.  *
  62.  *    Revision 2.4  1992/12/14  10:54:56  istewart
  63.  *    BETA 215 Fixes and 2.1 Release
  64.  *
  65.  *    Revision 2.3  1992/11/06  10:03:44  istewart
  66.  *    214 Beta test updates
  67.  *
  68.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  69.  *    Beta 212 Baseline
  70.  *
  71.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  72.  *    211 Beta updates
  73.  *
  74.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  75.  *    MS-Shell 2.0 Baseline release
  76.  *
  77.  */
  78.  
  79. #if defined (__EMX__)
  80. #  include <sys/emx.h>
  81. #endif
  82. #include <sys/types.h>
  83. #include <sys/stat.h>
  84. #include <stdio.h>
  85. #include <signal.h>
  86. #include <errno.h>
  87. #include <setjmp.h>
  88. #include <stdlib.h>
  89. #include <fcntl.h>
  90. #include <string.h>
  91. #include <unistd.h>
  92. #include <limits.h>
  93. #include <dirent.h>
  94. #include <ctype.h>
  95. #include <stdarg.h>
  96. #if defined (__EMX__)
  97. #  include <sys/ioctl.h>
  98. #endif
  99.  
  100.  
  101. /*
  102.  * There appears to be no alloca in TurboC
  103.  */
  104.  
  105. #if defined (__TURBOC__)
  106. #  include <alloc.h>
  107. #  include <dir.h>
  108. #  define alloca(x)        malloc (x)
  109. #  define alloca_free(x)    free (x)
  110. #else
  111. #  include <malloc.h>
  112. #  define alloca_free(x)
  113. #endif
  114.  
  115. #include "sh.h"
  116.  
  117. #define MAX_LINE    160        /* Max line length        */
  118.  
  119. #define F_START        4
  120. #define    NSTART        16        /* default number of words to    */
  121.                     /* allow for initially        */
  122. #define IS_OCTAL(a)    (((a) >= '0') && ((a) <= '7'))
  123.  
  124. static char    *nopipe = "can't create pipe";
  125.  
  126. /* List of open files to allow us to simulate the Unix open and unlink
  127.  * operation for temporary files
  128.  */
  129.  
  130. typedef struct flist {
  131.     struct flist    *fl_next;    /* Next link            */
  132.     char        *fl_name;    /* File name            */
  133.     bool        fl_close;    /* Delete on close flag        */
  134.     int            fl_size;    /* Size of fl_fd array        */
  135.     int            fl_count;    /* Number of entries in array    */
  136.     int            fl_mode;    /* File open mode        */
  137.     int            *fl_fd;        /* File ID array (for dup)    */
  138. } s_flist;
  139.  
  140. static s_flist            *list_start = (s_flist *)NULL;
  141. static s_flist * F_LOCAL    find_entry (int);
  142. static void F_LOCAL        SaveFileDescriptor (FILE *);
  143. static char * F_LOCAL        _Ex_SkipWhiteSpace (char *);
  144. #if (OS_TYPE != OS_UNIX)
  145. static void F_LOCAL        _Ex_ExpandIndirectFile (char *, Word_B **);
  146. static char * F_LOCAL        _Ex_GetSpace (int, char *);
  147. static void F_LOCAL        _Ex_ExpandField (char *, Word_B **);
  148. static char * F_LOCAL        _Ex_ConvertEnvVariables (char *);
  149. static void F_LOCAL        _Ex_SaveArgvValue (char *, bool, Word_B **);
  150. static void F_LOCAL        _Ex_CommandLine (char *, Word_B **);
  151. static unsigned long F_LOCAL    QueryApplicationType1 (int);
  152. #endif
  153.  
  154. #if (OS_TYPE == OS_OS2)
  155. static void F_LOCAL        _Ex_ProcessEMXArguments (char *, Word_B **);
  156. #endif
  157.  
  158. #if (OS_TYPE == OS_DOS) && (__WATCOMC__)
  159. extern    char            *_LpCmdLine;
  160. extern    char            *_LpPgmName;
  161. static void F_LOCAL        OutputBIOSString (char *);
  162. #endif
  163.  
  164. #if (__WATCOMC__)
  165. char                *_getdcwd (int, char *, int);
  166. #endif
  167.  
  168. #if (__EMX__)
  169. char                *_getdcwd (int, char *, int);
  170. #endif
  171.  
  172. /*
  173.  * Command Line pointers
  174.  */
  175.  
  176. #if defined (__TURBOC__)    /* Borland C                */
  177. #  define ARG_ARRAY    _argv
  178. #  define ARG_COUNT    _argc
  179. #  define ENTRY_POINT    _setargv
  180. #elif defined (__WATCOMC__)    /* WatCom C                */
  181. #  define ARG_ARRAY    ___Argv
  182. #  define ARG_COUNT    ___Argc
  183. #  define ENTRY_POINT    __Init_Argv
  184. #elif defined (__IBMC__)    /* IBM C Set/2                */
  185. #  define ARG_ARRAY    _argv
  186. #  define ARG_COUNT    _argc
  187. #  define ENTRY_POINT    _setuparg
  188. #elif defined (__EMX__)        /* Gcc/EMX                */
  189. #  define ARG_ARRAY    _argv
  190. #  define ARG_COUNT    _argc
  191. #  define ENTRY_POINT    GetArgcV
  192. #elif defined (MSDOS)        /* Microsoft C                */
  193. #  define ARG_ARRAY    __argv
  194. #  define ARG_COUNT    __argc
  195. #  define ENTRY_POINT    _setargv
  196. #endif
  197.  
  198. #if (OS_TYPE != OS_UNIX)
  199. #  if defined (__EMX__)            /* Gcc/EMX             */
  200. extern void    __startup (int argc, char **argv);
  201. extern void    ENTRY_POINT (void);
  202. static char    **ARG_ARRAY;         /* Current argument address    */
  203. static int    ARG_COUNT;         /* Current argument count    */
  204. #  else
  205. extern void    ENTRY_POINT (void);
  206. extern char    **ARG_ARRAY;         /* Current argument address    */
  207. extern int    ARG_COUNT;         /* Current argument count    */
  208. #  endif
  209.  
  210. /*
  211.  * Some EMX startup statics
  212.  */
  213.  
  214. #  if defined (__EMX__)
  215.                     /* The version of this libc.*/
  216. static const char    _libc_version[] = " libc for emx 0.8h";
  217. /* Display this warning if emx.dll or emx.exe is out of date. */
  218. static const char    _version_warning[] = 
  219.                 "WARNING: emx 0.8h or later required\r\n";
  220.                     /* The buffer for stdin. */
  221. static char        ibuf[BUFSIZ];
  222. #  endif
  223.  
  224. /*
  225.  * General OS independent start of the command line and program name.
  226.  */
  227.  
  228. char         *_ACmdLine;
  229. char         *_APgmName;         /* Program name            */
  230.  
  231. #  if defined (OS2)
  232. extern ushort far _aenvseg;
  233. extern ushort far _acmdln;
  234. #  endif
  235.  
  236. /*
  237.  * Directory functions internals structure.
  238.  */
  239.  
  240. typedef struct _dircontents    DIRCONT;
  241. static void            FreeDirectoryListing (DIRCONT *);
  242. #endif
  243.  
  244. /*
  245.  * Open a file and add it to the Open file list.  Errors are the same as
  246.  * for a normal open.
  247.  */
  248.  
  249. int S_open (bool d_flag, char *name, int mode)
  250. {
  251.     int            pmask;
  252.     s_flist        *fp = (s_flist *)NULL;
  253.     int            *f_list = (int *)NULL;
  254.     char        *f_name = (char *)NULL;
  255.     void        (*save_signal)(int);
  256. #ifdef __IBMC__
  257.     HFILE        shfFileHandle;
  258.     ULONG        ActionTaken;
  259.     ULONG        ulFileAttribute;
  260.     ULONG        ulOpenFlag;
  261.     ULONG        ulOpenMode;
  262.     APIRET        rc;            /* Return code */
  263. #endif
  264.  
  265. /* Check this is a valid file name */
  266.  
  267.     CheckDOSFileName (name);
  268.  
  269. /* Grap some space.  If it fails, free space and return an error */
  270.  
  271.     if (((fp = (s_flist *) GetAllocatedSpace (sizeof (s_flist)))
  272.         == (s_flist *)NULL) ||
  273.     ((f_list = (int *) GetAllocatedSpace (sizeof (int) * F_START))
  274.         == (int *)NULL) ||
  275.     ((f_name = StringSave (name)) == null))
  276.     {
  277.     if (f_list == (int *)NULL)
  278.         ReleaseMemoryCell ((void *)f_list);
  279.  
  280.     if (fp == (s_flist *)NULL)
  281.         ReleaseMemoryCell ((void *)fp);
  282.  
  283.     return -1;
  284.     }
  285.  
  286. /* Disable signals */
  287.  
  288.     save_signal = signal (SIGINT, SIG_IGN);
  289.  
  290. /* Set up the structure.  Change two Unix device names to the DOS
  291.  * equivalents and disable create
  292.  */
  293.  
  294. #if (OS_TYPE != OS_UNIX)
  295.     if (strnicmp (name, DeviceNameHeader, LEN_DEVICE_NAME_HEADER) == 0)
  296.     {
  297.     if (stricmp (&name[5], "tty") == 0)
  298.         strcpy (&name[5], "con");
  299.  
  300.     else if (stricmp (&name[5], "null") == 0)
  301.         strcpy (&name[5], "nul");
  302.  
  303.     mode &= ~(O_CREAT | O_TRUNC);
  304.     }
  305. #endif
  306.  
  307.     fp->fl_name  = strcpy (f_name, name);
  308.     fp->fl_close = d_flag;
  309.     fp->fl_size  = F_START;
  310.     fp->fl_count = 1;
  311.     fp->fl_fd    = f_list;
  312.     fp->fl_mode  = mode;
  313.  
  314. /*
  315.  * The IBM C Set/2 OS/2 2.x library function, open, does not appear work
  316.  * correctly.  So I've replaced it with a emulator here
  317.  */
  318.  
  319. #ifdef __IBMC__
  320.  
  321. /* Set up open actions */
  322.  
  323.     if (mode & O_CREAT)
  324.      ulOpenFlag = OPEN_ACTION_CREATE_IF_NEW |
  325.             ((mode & O_EXCL)
  326.                 ? OPEN_ACTION_FAIL_IF_EXISTS
  327.                 : ((mode & O_TRUNC)
  328.                     ? OPEN_ACTION_REPLACE_IF_EXISTS
  329.                     : OPEN_ACTION_OPEN_IF_EXISTS));
  330.     else
  331.      ulOpenFlag = OPEN_ACTION_FAIL_IF_NEW |
  332.              ((mode & O_TRUNC)
  333.                 ? OPEN_ACTION_REPLACE_IF_EXISTS
  334.                 : OPEN_ACTION_OPEN_IF_EXISTS);
  335.  
  336. /* Set up open mode */
  337.  
  338.     ulOpenMode = OPEN_FLAGS_SEQUENTIAL | OPEN_SHARE_DENYNONE;
  339.  
  340.     if (mode & O_NOINHERIT)
  341.     ulOpenMode |= OPEN_FLAGS_NOINHERIT;
  342.  
  343.     if (mode & O_WRONLY)
  344.     ulOpenMode |= OPEN_ACCESS_WRITEONLY;
  345.  
  346.     else if (mode & O_RDONLY)
  347.     ulOpenMode |= OPEN_ACCESS_READONLY;
  348.  
  349.     else
  350.     ulOpenMode |= OPEN_ACCESS_READWRITE;
  351.  
  352.     DISABLE_HARD_ERRORS;
  353.     rc = DosOpen (name, &shfFileHandle, &ActionTaken, (ULONG)0, FILE_NORMAL,
  354.               ulOpenFlag, ulOpenMode, (PEAOP2)NULL);
  355.     ENABLE_HARD_ERRORS;
  356.  
  357.     if (!rc)
  358.     {
  359.     if (mode & O_TEXT)
  360.         setmode (shfFileHandle, O_TEXT);
  361.  
  362.     else if (mode & O_BINARY)
  363.         setmode (shfFileHandle, O_BINARY);
  364.  
  365.     fp->fl_fd[0] = shfFileHandle;
  366.     }
  367.  
  368.     else
  369.     fp->fl_fd[0] = -1;
  370.  
  371. #else
  372.     DISABLE_HARD_ERRORS;
  373.     fp->fl_fd[0] = open (name, mode, S_IREAD | S_IWRITE);
  374.     ENABLE_HARD_ERRORS;
  375. #endif
  376.  
  377. /* Open the file */
  378.  
  379.     if (fp->fl_fd[0] < 0)
  380.     {
  381.     ReleaseMemoryCell ((void *)f_name);
  382.     ReleaseMemoryCell ((void *)f_list);
  383.     ReleaseMemoryCell ((void *)fp);
  384.     pmask = -1;
  385.     }
  386.  
  387. /* Make sure everything is in area 0 */
  388.  
  389.     else
  390.     {
  391.     SetMemoryAreaNumber ((void *)fp, 0);
  392.     SetMemoryAreaNumber ((void *)f_list, 0);
  393.  
  394. /* List into the list */
  395.  
  396.     fp->fl_next   = list_start;
  397.     list_start = fp;
  398.  
  399. /* Return the file descriptor */
  400.  
  401.     pmask = fp->fl_fd[0];
  402.  
  403. /* Mark in use */
  404.  
  405.     ChangeFileDescriptorStatus (pmask, TRUE);
  406.     }
  407.  
  408. /* Restore signals */
  409.  
  410.     signal (SIGINT, save_signal);
  411.  
  412.     return pmask;
  413. }
  414.  
  415. /*
  416.  * Scan the File list for the appropriate entry for the specified ID
  417.  */
  418.  
  419. static s_flist * F_LOCAL find_entry (int fid)
  420. {
  421.     s_flist    *fp = list_start;
  422.     int        i;
  423.  
  424.     while (fp != (s_flist *)NULL)
  425.     {
  426.     for (i = 0; i < fp->fl_count; i++)
  427.     {
  428.         if (fp->fl_fd[i] == fid)
  429.         return fp;
  430.     }
  431.  
  432.     fp = fp->fl_next;
  433.     }
  434.  
  435.     return (s_flist *)NULL;
  436. }
  437.  
  438. /* Close the file
  439.  *
  440.  * We need a version of close that does everything but close for dup2 as
  441.  * new file id is closed.  If c_flag is TRUE, close the file as well.
  442.  */
  443.  
  444. int    S_close (int fid, bool c_flag)
  445. {
  446.     s_flist    *fp = find_entry (fid);
  447.     s_flist    *last = (s_flist *)NULL;
  448.     s_flist    *fp1 = list_start;
  449.     int        i;
  450.     bool    release = TRUE;
  451.     bool    delete = FALSE;
  452.     char    *fname;
  453.     void    (*save_signal)(int);
  454.  
  455. /* Disable signals */
  456.  
  457.     save_signal = signal (SIGINT, SIG_IGN);
  458.  
  459. /* Find the entry for this ID */
  460.  
  461.     if (fp != (s_flist *)NULL)
  462.     {
  463.     for (i = 0; i < fp->fl_count; i++)
  464.     {
  465.         if (fp->fl_fd[i] == fid)
  466.         fp->fl_fd[i] = -1;
  467.  
  468.         if (fp->fl_fd[i] != -1)
  469.         release = FALSE;
  470.     }
  471.  
  472. /* Are all the Fids closed ? */
  473.  
  474.     if (release)
  475.     {
  476.         fname = fp->fl_name;
  477.         delete = fp->fl_close;
  478.         ReleaseMemoryCell ((void *)fp->fl_fd);
  479.  
  480. /* Scan the list and remove the entry */
  481.  
  482.         while (fp1 != (s_flist *)NULL)
  483.         {
  484.         if (fp1 != fp)
  485.         {
  486.             last = fp1;
  487.             fp1 = fp1->fl_next;
  488.             continue;
  489.         }
  490.  
  491.         if (last == (s_flist *)NULL)
  492.             list_start = fp->fl_next;
  493.  
  494.         else
  495.             last->fl_next = fp->fl_next;
  496.  
  497.         break;
  498.         }
  499.  
  500. /* OK - delete the area */
  501.  
  502.         ReleaseMemoryCell ((void *)fp);
  503.     }
  504.     }
  505.  
  506. /* Flush these two in case they were re-directed */
  507.  
  508.     FlushStreams ();
  509.  
  510. /* Close the file anyway */
  511.  
  512.     if (c_flag)
  513.     {
  514.     i = close (fid);
  515.     ChangeFileDescriptorStatus (fid, FALSE);
  516.     }
  517.  
  518. /* Delete the file ? */
  519.  
  520.     if (delete)
  521.     {
  522.     unlink (fname);
  523.     ReleaseMemoryCell ((void *)fname);
  524.     }
  525.  
  526. /* Restore signals */
  527.  
  528.     signal (SIGINT, save_signal);
  529.  
  530.     return i;
  531. }
  532.  
  533. /*
  534.  * Version of fclose
  535.  */
  536.  
  537. void    S_fclose (FILE *FP, bool d_flag)
  538. {
  539.     CloseFile (FP);
  540.     S_close (fileno (FP), d_flag);
  541. }
  542.  
  543. /*
  544.  * Duplicate file handler.  Add the new handler to the ID array for this
  545.  * file.
  546.  */
  547.  
  548. int S_dup (int old_fid)
  549. {
  550.     int        new_fid;
  551.  
  552.     if ((new_fid = dup (old_fid)) >= 0)
  553.     S_Remap (old_fid, new_fid);
  554.  
  555.     return new_fid;
  556. }
  557.  
  558. /*
  559.  * Add the ID to the ID array for this file
  560.  */
  561.  
  562. int S_Remap (int old_fid, int new_fid)
  563. {
  564.     s_flist    *fp = find_entry (old_fid);
  565.     int        *flist;
  566.     int        i;
  567.  
  568.     ChangeFileDescriptorStatus (new_fid, TRUE);
  569.  
  570.     if (fp == (s_flist *)NULL)
  571.     return new_fid;
  572.  
  573. /* Is there an empty slot ? */
  574.  
  575.     for (i = 0; i < fp->fl_count; i++)
  576.     {
  577.     if (fp->fl_fd[i] == -1)
  578.         return (fp->fl_fd[i] = new_fid);
  579.     }
  580.  
  581. /* Is there any room at the end ? No - grap somemore space and effect a
  582.  * re-alloc.  What to do if the re-alloc fails - should really get here.
  583.  * Safty check only??
  584.  */
  585.  
  586.     if (fp->fl_count == fp->fl_size)
  587.     {
  588.     flist = (int *)ReAllocateSpace ((void *)fp->fl_fd,
  589.                     (fp->fl_size + F_START) * sizeof (int));
  590.  
  591.     if (flist == (int *)NULL)
  592.         return new_fid;
  593.  
  594.     SetMemoryAreaNumber ((void *)flist, 0);
  595.     fp->fl_fd   = flist;
  596.     fp->fl_size += F_START;
  597.     }
  598.  
  599.     return (fp->fl_fd[fp->fl_count++] = new_fid);
  600. }
  601.  
  602.  
  603. /*
  604.  * Duplicate file handler onto specific handler
  605.  */
  606.  
  607. int S_dup2 (int old_fid, int new_fid)
  608. {
  609.     int        res = -1;
  610.     int        i;
  611.     Save_IO    *sp;
  612.  
  613. /* If duping onto stdin, stdout or stderr, Search the Save IO stack for an
  614.  * entry matching us
  615.  */
  616.  
  617.     if ((new_fid >= STDIN_FILENO) && (new_fid <= STDERR_FILENO))
  618.     {
  619.     for (sp = SSave_IO, i = 0;
  620.          (i < NSave_IO_E) && (SSave_IO[i].depth < Execute_stack_depth);
  621.          i++, ++sp)
  622.         continue;
  623.  
  624. /* If depth is greater the Execute_stack_depth - we should panic as this
  625.  * should not happen.  However, for the moment, I'll ignore it
  626.  */
  627.  
  628. /* If there an entry for this depth ? */
  629.  
  630.     if (i == NSave_IO_E)
  631.     {
  632.  
  633. /* Do we need more space? */
  634.  
  635.         if (NSave_IO_E == MSave_IO_E)
  636.         {
  637.         sp = (Save_IO *)ReAllocateSpace ((MSave_IO_E != 0)
  638.                             ? SSave_IO : (void *)NULL,
  639.                          (MSave_IO_E + SSAVE_IO_SIZE) *
  640.                             sizeof (Save_IO));
  641.  
  642. /* Check for error */
  643.  
  644.         if (sp == (Save_IO *)NULL)
  645.             return -1;
  646.  
  647. /* Save original data */
  648.  
  649.         SetMemoryAreaNumber ((void *)sp, 1);
  650.         SSave_IO = sp;
  651.         MSave_IO_E += SSAVE_IO_SIZE;
  652.         }
  653.  
  654. /* Initialise the new entry */
  655.  
  656.         sp = &SSave_IO[NSave_IO_E++];
  657.         sp->depth             = Execute_stack_depth;
  658.         sp->fp[STDIN_FILENO]  = -1;
  659.         sp->fp[STDOUT_FILENO] = -1;
  660.         sp->fp[STDERR_FILENO] = -1;
  661.     }
  662.  
  663.     if (sp->fp[new_fid] == -1)
  664.         sp->fp[new_fid] = ReMapIOHandler (new_fid);
  665.  
  666.     FlushStreams ();
  667.     }
  668.  
  669. /* OK - Dup the descriptor */
  670.  
  671.     if ((old_fid != -1) && ((res = dup2 (old_fid, new_fid)) >= 0))
  672.     {
  673.     S_close (new_fid, FALSE);
  674.     res = S_Remap (old_fid, new_fid);
  675.     }
  676.  
  677.     return res;
  678. }
  679.  
  680. /*
  681.  * Restore the Stdin, Stdout and Stderr to original values.  If change is
  682.  * FALSE, just remove entries from stack.  A special case for exec.
  683.  */
  684.  
  685. int RestoreStandardIO (int rv, bool change)
  686. {
  687.     int        j, i;
  688.     Save_IO    *sp;
  689.  
  690. /* Start at the top and remove any entries above the current execute stack
  691.  * depth
  692.  */
  693.  
  694.     for (j = NSave_IO_E; j > 0; j--)
  695.     {
  696.        sp = &SSave_IO[j - 1];
  697.  
  698.        if (sp->depth < Execute_stack_depth)
  699.        break;
  700.  
  701. /* Reduce number of entries */
  702.  
  703.     --NSave_IO_E;
  704.  
  705. /* If special case (changed at this level) - continue */
  706.  
  707.     if (!change && (sp->depth == Execute_stack_depth))
  708.         continue;
  709.  
  710. /* Close and restore any files */
  711.  
  712.     for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
  713.     {
  714.         if (sp->fp[i] != -1)
  715.         {
  716.         S_close (i, TRUE);
  717.         dup2 (sp->fp[i], i);
  718.         S_close (sp->fp[i], TRUE);
  719.         }
  720.     }
  721.     }
  722.  
  723.     return rv;
  724. }
  725.  
  726. /*
  727.  * Create a Pipe
  728.  */
  729.  
  730. int OpenAPipe (void)
  731. {
  732.     int        i;
  733.  
  734.     if ((i = S_open (TRUE, GenerateTemporaryFileName (), O_PMASK)) < 0)
  735.     PrintErrorMessage (nopipe);
  736.  
  737.     return i;
  738. }
  739.  
  740. /*
  741.  * Close a pipe
  742.  */
  743.  
  744. void CloseThePipe (int pv)
  745. {
  746.     if (pv != -1)
  747.     S_close (pv, TRUE);
  748. }
  749.  
  750. /*
  751.  * Check for restricted shell
  752.  */
  753.  
  754. bool CheckForRestrictedShell (char *s)
  755. {
  756.     if (RestrictedShellFlag)
  757.     {
  758.     PrintErrorMessage (BasicErrorMessage, s, "restricted");
  759.     return TRUE;
  760.     }
  761.  
  762.     return FALSE;
  763. }
  764.  
  765. /*
  766.  * Check to see if a file is a shell script.  If it is, return the file
  767.  * handler for the file
  768.  */
  769. static char *Extensions[] = { null, SHELLExtension, KSHELLExtension};
  770.  
  771. int OpenForExecution (char *path, char **params, int *nargs)
  772. {
  773.     int        RetVal = -1;
  774.     int        j;
  775.     int        EndP = strlen (path);
  776.     char    *local_path;
  777.  
  778. /* Work on a copy of the path */
  779.  
  780.     if ((local_path = AllocateMemoryCell (EndP + 5)) == (char *)NULL)
  781.     return -1;
  782.  
  783.     strcpy (local_path, path);
  784.  
  785. /* Try the file name and then with a .sh and then .ksh appended */
  786.  
  787.     for (j = 0; j < 3; j++)
  788.     {
  789.         strcpy (&local_path[EndP], Extensions[j]);
  790.  
  791.     if ((RetVal = CheckForScriptFile (local_path, params, nargs)) >= 0)
  792.         break;
  793.     }
  794.  
  795.     ReleaseMemoryCell ((void *)local_path);
  796.     return RetVal;
  797. }
  798.  
  799. /*
  800.  * Check for shell script
  801.  */
  802.  
  803. int CheckForScriptFile (char *path, char **params, int *nargs)
  804. {
  805.     char    buf[512];        /* Input buffer            */
  806.     int        fp;            /* File handler            */
  807.     int        nbytes;            /* Number of bytes read        */
  808.     char    *bp;            /* Pointers into buffers    */
  809.     char    *ep;
  810.  
  811.     if ((fp = S_open (FALSE, path, O_RMASK)) < 0)
  812.     return -1;
  813.  
  814. /* zero or less bytes - not a script */
  815.  
  816.     memset (buf, 0, 512);
  817.     nbytes = read (fp, buf, 512);
  818.     lseek (fp, 0L, SEEK_SET);
  819.  
  820.     for (ep = &buf[nbytes], bp = buf;
  821.      (bp < ep) && ((unsigned char)*bp >= 0x08); ++bp)
  822.     continue;
  823.  
  824. /* If non-ascii file or length is less than 1 - not a script */
  825.  
  826.     if ((bp != ep) || (nbytes < 1))
  827.     {
  828.     S_close (fp, TRUE);
  829.     return -1;
  830.     }
  831.  
  832. /* Ensure end of buffer detected */
  833.  
  834.     buf[511] = 0;
  835.  
  836. /* Initialise the return parameters, if specified */
  837.  
  838.     if (params != (char **)NULL)
  839.     *params = null;
  840.  
  841.     if (nargs != (int *)NULL)
  842.     *nargs = 0;
  843.  
  844. /* We don't care how many bytes were read now, so use it to count the
  845.  * additional arguments
  846.  */
  847.  
  848.     nbytes = 0;
  849.  
  850. /* Find the end of the first line */
  851.  
  852.     if ((bp = strchr (buf, CHAR_NEW_LINE)) != (char *)NULL)
  853.     *bp = 0;
  854.  
  855.     bp = buf;
  856.     ep = (char *)NULL;
  857.  
  858. /* Check for script */
  859.  
  860.     if ((*(bp++) != CHAR_COMMENT) || (*(bp++) != '!'))
  861.     return fp;
  862.  
  863.     while (*bp)
  864.     {
  865.  
  866. /* Save the start of the arguments */
  867.  
  868.     if (*(bp = _Ex_SkipWhiteSpace (bp)))
  869.     {
  870.         if (ep == (char *)NULL)
  871.         ep = bp;
  872.  
  873. /* Count the arguments */
  874.  
  875.         ++nbytes;
  876.     }
  877.  
  878.     bp = SkipToWhiteSpace (bp);
  879.     }
  880.  
  881. /* Set up the return parameters, if appropriate */
  882.  
  883.     if ((params != (char **)NULL) && (strlen (ep) != 0))
  884.     {
  885.     if ((*params = AllocateMemoryCell (strlen (ep) + 1)) == (char *)NULL)
  886.     {
  887.         *params = null;
  888.         S_close (fp, TRUE);
  889.         return -1;
  890.     }
  891.  
  892.     strcpy (*params, ep);
  893.     }
  894.  
  895.     if (nargs != (int *)NULL)
  896.     *nargs = nbytes;
  897.  
  898.     return fp;
  899. }
  900.  
  901. /*
  902.  * Get the file descriptor type.
  903.  */
  904.  
  905. #if (OS_TYPE == OS_OS2)
  906. int     GetDescriptorType (int fp)
  907. {
  908.     OSCALL_PARAM    fsType = HANDTYPE_FILE;
  909.     OSCALL_PARAM    usDeviceAttr;
  910.  
  911.     if (DosQHandType (fp, &fsType, &usDeviceAttr))
  912.     return DESCRIPTOR_UNKNOWN;
  913.  
  914.     if (LOBYTE (fsType) == HANDTYPE_PIPE)
  915.         return DESCRIPTOR_PIPE;
  916.  
  917.     else if (LOBYTE (fsType) != HANDTYPE_DEVICE)
  918.         return DESCRIPTOR_FILE;
  919.  
  920. /* The value 0x8083 seems to be the value returned by the console */
  921.  
  922.     else if (usDeviceAttr == 0x8083)
  923.         return DESCRIPTOR_CONSOLE;
  924.  
  925.     else
  926.         return DESCRIPTOR_DEVICE;
  927. }
  928. #endif
  929.  
  930. /* NT version */
  931.  
  932. #if (OS_TYPE == OS_NT)
  933.  
  934. int     GetDescriptorType (int fp)
  935. {
  936.     extern long _CRTAPI1 _get_osfhandle (int);
  937.     DWORD        fdwMode;
  938.     HANDLE        osfp = (HANDLE)_get_osfhandle (fp);
  939.  
  940.     switch (GetFileType (osfp))
  941.     {
  942.     default:
  943.         return DESCRIPTOR_UNKNOWN;
  944.  
  945.     case FILE_TYPE_DISK:
  946.         return DESCRIPTOR_FILE;
  947.     
  948.     case FILE_TYPE_PIPE:
  949.         return DESCRIPTOR_PIPE;
  950.  
  951.     case FILE_TYPE_CHAR:
  952.     {
  953.         if (!GetConsoleMode (osfp, &fdwMode))
  954.         return DESCRIPTOR_DEVICE;
  955.  
  956.         else
  957.         return DESCRIPTOR_CONSOLE;
  958.     }
  959.     }
  960. }
  961. #endif
  962.  
  963. /* DOS Version */
  964.  
  965. #if (OS_TYPE == OS_DOS)
  966. int     GetDescriptorType (int fp)
  967. {
  968.     union REGS    r;
  969.  
  970.     r.x.REG_AX = 0x4400;
  971.     r.x.REG_BX = fp;
  972.     DosInterrupt (&r, &r);
  973.  
  974.     if (r.x.REG_CFLAGS)
  975.     return DESCRIPTOR_UNKNOWN;
  976.  
  977.     if ((r.x.REG_DX & 0x0080) == 0)
  978.         return DESCRIPTOR_FILE;
  979.  
  980.     if ((r.x.REG_DX & 0x0081) != 0x0081)
  981.     return DESCRIPTOR_DEVICE;
  982.  
  983. /*
  984.  * Check to see if the console is really the console or something else.
  985.  * Look at /dev/con and see if the console i/o bits are set
  986.  */
  987.  
  988.     r.x.REG_AX = 0x4400;
  989.     r.x.REG_BX = open ("con", 0);
  990.     DosInterrupt (&r, &r);
  991.     close (r.x.REG_BX);
  992.  
  993.     if ((r.x.REG_CFLAGS) || ((r.x.REG_DX & 0x0083) != 0x0083))
  994.     return DESCRIPTOR_DEVICE;
  995.     
  996.     return DESCRIPTOR_CONSOLE;
  997. }
  998. #endif
  999.  
  1000. /* UNIX Version */
  1001.  
  1002. #if (OS_TYPE == OS_UNIX)
  1003. int     GetDescriptorType (int fp)
  1004. {
  1005.     struct stat        s;
  1006.  
  1007.     if (fstat (fp, &s) != 0) 
  1008.     return DESCRIPTOR_UNKNOWN;
  1009.  
  1010.     else if (S_ISREG (s.st_mode))
  1011.         return DESCRIPTOR_FILE;
  1012.     
  1013.     else if (S_ISCHR (s.st_mode))
  1014.     return DESCRIPTOR_CONSOLE;
  1015.     
  1016.     return DESCRIPTOR_UNKNOWN;
  1017. }
  1018. #endif
  1019.  
  1020. /*
  1021.  * Get the current drive number
  1022.  */
  1023.  
  1024. #if (OS_TYPE == OS_OS2)
  1025. unsigned int GetCurrentDrive (void)
  1026. {
  1027.     OSCALL_PARAM    usDisk;
  1028.     ULONG        ulDrives;
  1029.  
  1030.     DosQCurDisk (&usDisk, &ulDrives);        /* gets current drive        */
  1031.  
  1032.     return (unsigned int)usDisk;
  1033. }
  1034. #endif
  1035.  
  1036. /* NT Version */
  1037.  
  1038. #if (OS_TYPE == OS_NT)
  1039. unsigned int GetCurrentDrive (void)
  1040. {
  1041.     char    szCurDir [MAX_PATH];
  1042.  
  1043.     GetCurrentDirectory (MAX_PATH, szCurDir);
  1044.  
  1045.     return szCurDir[0] - 'A' + 1;
  1046. }
  1047. #endif
  1048.  
  1049. /* DOS Version */
  1050.  
  1051. #if (OS_TYPE == OS_DOS)
  1052. unsigned int GetCurrentDrive (void)
  1053. {
  1054. #  if defined (__TURBOC__)
  1055.     return (unsigned int)getdisk () + 1;
  1056.  
  1057. #  elif defined (__EMX__)
  1058.  
  1059.     union REGS        r;
  1060.  
  1061.     r.h.ah = 0x19;
  1062.     DosInterrupt (&r, &r);
  1063.     return r.h.al + 1;
  1064.  
  1065. #  else
  1066.  
  1067.     unsigned int    CurrentDrive;
  1068.  
  1069.     _dos_getdrive (&CurrentDrive);
  1070.  
  1071.     return CurrentDrive;
  1072. #  endif
  1073. }
  1074. #endif
  1075.  
  1076. /*
  1077.  * Set the current drive number and return the number of drives.
  1078.  */
  1079.  
  1080. #if (OS_TYPE == OS_OS2)
  1081. int    SetCurrentDrive (unsigned int drive)
  1082. {
  1083.     OSCALL_RET        usError;
  1084.     OSCALL_PARAM    usDisk;
  1085.     FSALLOCATE        FsBlock;
  1086.     ULONG        ulDrives;
  1087.     int            i;
  1088.  
  1089. /* Check the drive exists */
  1090.  
  1091.     DISABLE_HARD_ERRORS;
  1092.     usError = DosQFSInfo (drive, FSIL_ALLOC, (PBYTE)&FsBlock,
  1093.                   sizeof (FSALLOCATE));
  1094.     ENABLE_HARD_ERRORS;
  1095.  
  1096.     if (usError || DosSelectDisk ((USHORT)drive))
  1097.         return -1;
  1098.  
  1099. /* Get the current disk and check that to see the number of drives */
  1100.  
  1101.     DosQCurDisk (&usDisk, &ulDrives);        /* gets current drive        */
  1102.  
  1103.     for (i = 25; (!(ulDrives & (1L << i))) && i >= 0; --i)
  1104.     continue;
  1105.  
  1106.     return i + 1;
  1107. }
  1108. #endif
  1109.  
  1110. /* NT Version */
  1111.  
  1112. #if (OS_TYPE == OS_NT)
  1113. int    SetCurrentDrive (unsigned int drive)
  1114. {
  1115.     char        szNewDrive[3];
  1116.     DWORD        dwLogicalDrives;
  1117.     int            i;
  1118.  
  1119.     szNewDrive[0] = drive + 'A' - 1;
  1120.     szNewDrive[1] = CHAR_DRIVE;
  1121.     szNewDrive[2] = 0;
  1122.  
  1123.     if (!SetCurrentDirectory (szNewDrive))
  1124.     return -1;
  1125.  
  1126.     dwLogicalDrives = GetLogicalDrives();
  1127.  
  1128.     for (i = 25; (!(dwLogicalDrives & (1L << i))) && i >= 0; --i)
  1129.     continue;
  1130.  
  1131.     return i + 1;
  1132. }
  1133. #endif
  1134.  
  1135. /* DOS Version */
  1136.  
  1137. #if (OS_TYPE == OS_DOS)
  1138. int    SetCurrentDrive (unsigned int drive)
  1139. {
  1140. #  if defined (__TURBOC__)
  1141.  
  1142.    return setdisk (drive - 1);
  1143.  
  1144. #  elif defined (__EMX__)
  1145.  
  1146.     union REGS        r;
  1147.  
  1148.     r.h.ah = 0x0e;
  1149.     r.h.dl = drive;
  1150.     DosInterrupt (&r, &r);
  1151.     return r.h.al;
  1152.  
  1153. #  else
  1154.  
  1155.     unsigned int    ndrives;
  1156.  
  1157.     _dos_setdrive (drive, &ndrives);
  1158.  
  1159.     return (int)ndrives;
  1160. #  endif
  1161. }
  1162. #endif
  1163.  
  1164. /*
  1165.  * Get and process configuration line:
  1166.  *
  1167.  * <field> = <field> <field> # comment
  1168.  *
  1169.  * return the number of fields found.
  1170.  */
  1171.  
  1172. int    ExtractFieldsFromLine (LineFields *fd)
  1173. {
  1174.     char    *cp;
  1175.     int        len;
  1176.     Word_B    *wb;
  1177.  
  1178.     if (fgets (fd->Line, fd->LineLength - 1, fd->FP) == (char *)NULL)
  1179.     {
  1180.     CloseFile (fd->FP);
  1181.     return -1;
  1182.     }
  1183.  
  1184. /* Remove the EOL */
  1185.  
  1186.     if ((cp = strchr (fd->Line, CHAR_NEW_LINE)) != (char *)NULL)
  1187.     *cp = 0;
  1188.  
  1189. /* Remove the comments at end */
  1190.  
  1191.     if ((cp = strchr (fd->Line, CHAR_COMMENT)) != (char *)NULL)
  1192.     *cp = 0;
  1193.  
  1194. /* Extract the fields */
  1195.  
  1196.     if (fd->Fields != (Word_B *)NULL)
  1197.     fd->Fields->w_nword = 0;
  1198.  
  1199.     fd->Fields = SplitString (fd->Line, fd->Fields);
  1200.  
  1201.     if (WordBlockSize (fd->Fields) < 2)
  1202.     return 1;
  1203.  
  1204. /* Check for =.  At end of first field? */
  1205.  
  1206.     wb = fd->Fields;
  1207.     len = strlen (wb->w_words[0]) - 1;
  1208.  
  1209.     if (wb->w_words[0][len] == CHAR_ASSIGN)
  1210.     wb->w_words[0][len] = 0;
  1211.  
  1212. /* Check second field for just being equals */
  1213.  
  1214.     if (strcmp (wb->w_words[1], "=") == 0)
  1215.     {
  1216.     if ((--(wb->w_nword)) > 1)
  1217.         memcpy (wb->w_words + 1, wb->w_words + 2,
  1218.             (wb->w_nword - 1) * sizeof (void *));
  1219.     }
  1220.  
  1221. /* Check the second field for starting with an equals */
  1222.  
  1223.     else if (*(wb->w_words[1]) == CHAR_ASSIGN)
  1224.     strcpy (wb->w_words[1], wb->w_words[1] + 1);
  1225.  
  1226.     return wb->w_nword;
  1227. }
  1228.  
  1229. /*
  1230.  * Split the string up into words
  1231.  */
  1232.  
  1233. Word_B *SplitString (char *string, Word_B *wb)
  1234. {
  1235.     while (*string)
  1236.     {
  1237.     while (isspace (*string))
  1238.         *(string++) = 0;
  1239.  
  1240.     if (*string)
  1241.         wb = AddWordToBlock (string, wb);
  1242.  
  1243.     string = SkipToWhiteSpace (string);
  1244.     }
  1245.  
  1246.     return wb;
  1247. }
  1248.  
  1249. /*
  1250.  * Test to see if a file is a directory
  1251.  */
  1252.  
  1253. bool    IsDirectory (char *Name)
  1254. {
  1255.     struct stat        s;
  1256.  
  1257.     return C2bool (S_stat (Name, &s) && S_ISDIR (s.st_mode & S_IFMT));
  1258. }
  1259.  
  1260. /*
  1261.  * Check that filename conforms to DOS format.  Convert additional .'s to ~.
  1262.  */
  1263.  
  1264. #if (OS_TYPE != OS_UNIX)
  1265. char *CheckDOSFileName (char *name)
  1266. {
  1267.     char    *s;
  1268.     char    *s1;
  1269.     int        count = 0;
  1270.  
  1271.     if (!IsHPFSFileSystem (name))
  1272.     {
  1273.  
  1274. /* Find start of file name */
  1275.  
  1276.     if ((s = FindLastPathCharacter (name)) == (char *)NULL)
  1277.         s = name;
  1278.  
  1279.     else
  1280.         ++s;
  1281.  
  1282. /* Skip over the directory entries */
  1283.  
  1284.     if ((strcmp (s, CurrentDirLiteral) == 0) ||
  1285.         (strcmp (s, ParentDirLiteral) == 0))
  1286.         /*SKIP*/;
  1287.  
  1288. /* Name starts with a dot? */
  1289.  
  1290.     else if (*s == CHAR_PERIOD)
  1291.         count = 2;
  1292.  
  1293. /* Count the number of dots */
  1294.  
  1295.     else
  1296.     {
  1297.         s1 = s;
  1298.  
  1299.         while ((s1 = strchr (s1, CHAR_PERIOD)) != (char *)NULL)
  1300.         {
  1301.         count++;
  1302.         s1++;
  1303.         }
  1304.     }
  1305.  
  1306. /* Check the dot count */
  1307.  
  1308.     if (count > 1)
  1309.     {
  1310.         if (!FL_TEST (FLAG_WARNING))
  1311.         fprintf (stderr, "sh: File <%s> has too many dots, changed to ",
  1312.              name);
  1313.  
  1314. /* Transform the very first if necessary */
  1315.  
  1316.         if (*s == CHAR_PERIOD)
  1317.         *s = CHAR_TILDE;
  1318.  
  1319.         s1 = s;
  1320.         count = 0;
  1321.  
  1322. /* Convert all except the first */
  1323.  
  1324.         while ((s1 = strchr (s1, CHAR_PERIOD)) != (char *)NULL)
  1325.         {
  1326.         if (++count != 1)
  1327.             *s1 = CHAR_TILDE;
  1328.  
  1329.         s1++;
  1330.         }
  1331.  
  1332.         if (!FL_TEST (FLAG_WARNING))
  1333.         PrintWarningMessage ("<%s>", name);
  1334.     }
  1335.     }
  1336.  
  1337. /* Check for double slashes */
  1338.  
  1339.     s = name;
  1340.  
  1341.     while ((s = FindPathCharacter (s)) != (char *)NULL)
  1342.     {
  1343.     if (IsPathCharacter (*(++s)))
  1344.         strcpy (s, s + 1);
  1345.     }
  1346.  
  1347.     return name;
  1348. }
  1349. #endif
  1350.  
  1351. /*
  1352.  * Get a valid numeric value
  1353.  */
  1354.  
  1355. bool    ConvertNumericValue (char *string, long *value, int base)
  1356. {
  1357.     char    *ep;
  1358.  
  1359.     *value = strtol (string, &ep, base);
  1360.  
  1361.     return C2bool (!*ep);
  1362. }
  1363.  
  1364. /*
  1365.  * Character types - replaces is???? functions
  1366.  */
  1367.  
  1368. void    SetCharacterTypes (char *String, int Type)
  1369. {
  1370.     int        i;
  1371.  
  1372. /*
  1373.  * If we're changing C_IFS (interfield separators), clear them all
  1374.  */
  1375.  
  1376.     if ((Type & C_IFS))
  1377.     {
  1378.     for (i = 0; i < UCHAR_MAX+1; i++)
  1379.         CharTypes[i] &=~ C_IFS;
  1380.  
  1381.     CharTypes[0] |= C_IFS;            /* include \0 as an C_IFS */
  1382.     }
  1383.  
  1384.  /*
  1385.   * Allow leading \0 in string
  1386.   */
  1387.  
  1388.     CharTypes[(unsigned char) *(String++)] |= Type;
  1389.  
  1390.     while (*String != 0)
  1391.     CharTypes[(unsigned char) *(String++)] |= Type;
  1392. }
  1393.  
  1394. /*
  1395.  * Initialise the Ctypes values
  1396.  */
  1397.  
  1398. void    InitialiseCharacterTypes ()
  1399. {
  1400.     int        c;
  1401.  
  1402.     memset (CharTypes, 0, UCHAR_MAX);
  1403.  
  1404.     for (c = 'a'; c <= 'z'; c++)
  1405.     CharTypes[c] = C_ALPHA;
  1406.  
  1407.     for (c = 'A'; c <= 'Z'; c++)
  1408.     CharTypes[c] = C_ALPHA;
  1409.  
  1410.     CharTypes['_'] = C_ALPHA;
  1411.     CharTypes[';'] = C_SEMICOLON;
  1412.     SetCharacterTypes ("0123456789", C_DIGIT);
  1413.     SetCharacterTypes ("\0 \t\n|&;<>()", C_LEX1);
  1414.     SetCharacterTypes ("*@#!$-?", C_VAR1);
  1415.     SetCharacterTypes ("=-+?#%", C_SUBOP);
  1416.     SetCharacterTypes ("?*[", C_WILD);
  1417. }
  1418.  
  1419. /*
  1420.  * Word Block Functions
  1421.  *
  1422.  * Add a new word to a Word Block or list
  1423.  */
  1424.  
  1425. Word_B *AddWordToBlock (char *wd, Word_B *wb)
  1426. {
  1427.  
  1428. /* Do we require more space ? */
  1429.  
  1430.     if ((wb == (Word_B *)NULL) || (wb->w_nword >= wb->w_bsize))
  1431.     {
  1432.     int    NewCount = (wb == (Word_B *)NULL) ? NSTART : wb->w_nword * 2;
  1433.  
  1434.         if ((wb = ReAllocateSpace (wb, (NewCount * sizeof (char *)) +
  1435.                      sizeof (Word_B))) == (Word_B *)NULL)
  1436.         return (Word_B *)NULL;
  1437.  
  1438.     wb->w_bsize = NewCount;
  1439.     }
  1440.  
  1441. /* Add to the list */
  1442.  
  1443.     wb->w_words[wb->w_nword++] = (void *)wd;
  1444.     return wb;
  1445. }
  1446.  
  1447. /*
  1448.  * Get the number of words in a block
  1449.  */
  1450.  
  1451. int    WordBlockSize (Word_B *wb)
  1452. {
  1453.     return (wb == (Word_B *)NULL) ? 0 : wb->w_nword;
  1454. }
  1455.  
  1456. /*
  1457.  * Convert a word block structure into a array of strings
  1458.  */
  1459.  
  1460. char    **GetWordList (Word_B *wb)
  1461. {
  1462.     char    **wd;
  1463.     int        nb;
  1464.  
  1465. /* If the word block is empty or does not exist, return no list */
  1466.  
  1467.     if (wb == (Word_B *)NULL)
  1468.     return NOWORDS;
  1469.  
  1470. /* Get some space for the array and set it up */
  1471.  
  1472.     if (((nb = sizeof (char *) * wb->w_nword) == 0) ||
  1473.     ((wd = (char **)GetAllocatedSpace (nb)) == (char **)NULL))
  1474.     {
  1475.     ReleaseMemoryCell ((void *)wb);
  1476.     return NOWORDS;
  1477.     }
  1478.  
  1479.     memcpy (wd, wb->w_words, nb);
  1480.     ReleaseMemoryCell ((void *)wb);    /* perhaps should done by caller */
  1481.     return wd;
  1482. }
  1483.  
  1484.  
  1485. /*
  1486.  * Build up the parameter variables
  1487.  */
  1488.  
  1489. Word_B *AddParameter (char *value, Word_B *wb, char *function)
  1490. {
  1491.     char    **NewArray;
  1492.     int        Count;
  1493.     int        i;
  1494.  
  1495. /* Add to block */
  1496.  
  1497.     if ((wb = AddWordToBlock (value, wb)) == (Word_B *)NULL)
  1498.     {
  1499.     fprintf (stderr, BasicErrorMessage, function, Outofmemory1);
  1500.     return (Word_B *)NULL;
  1501.     }
  1502.  
  1503. /* If not end, return */
  1504.  
  1505.     if (value != NOWORD)
  1506.     return wb;
  1507.  
  1508. /* Get number of entries */
  1509.  
  1510.     Count = wb->w_nword - 1;
  1511.  
  1512. /* Convert to array */
  1513.  
  1514.     if ((NewArray = GetWordList (wb)) == NOWORDS)
  1515.     {
  1516.     fprintf (stderr, BasicErrorMessage, function, Outofmemory1);
  1517.     return (Word_B *)NULL;
  1518.     }
  1519.  
  1520. /* Release old array.  Note: never release entry zero */
  1521.  
  1522.     if (ParameterArray != NOWORDS)
  1523.     {
  1524.     for (i = 1; i < ParameterCount; ++i)
  1525.         ReleaseMemoryCell ((void *)ParameterArray [i]);
  1526.  
  1527.     ReleaseMemoryCell ((void *)ParameterArray);
  1528.     }
  1529.  
  1530. /* Set new array to no-release */
  1531.  
  1532.     for (i = 0; i < Count; ++i)
  1533.     SetMemoryAreaNumber ((void *)NewArray[i], 0);
  1534.  
  1535.     SetMemoryAreaNumber ((void *)NewArray, 0);
  1536.  
  1537. /* Reset globals and environment */
  1538.  
  1539.     ParameterCount = Count - 1;
  1540.     ParameterArray = NewArray;
  1541.     SetVariableFromNumeric (ParameterCountVariable, (long)ParameterCount);
  1542.     return wb;
  1543. }
  1544.  
  1545. /*
  1546.  * Is this a valid variable name
  1547.  */
  1548.  
  1549. char    IsValidVariableName (char *s)
  1550. {
  1551.     if (!IS_VariableFC ((int)*s))
  1552.     return *s;
  1553.  
  1554.     while (*s && IS_VariableSC ((int)*s))
  1555.     ++s;
  1556.  
  1557.     return *s;
  1558. }
  1559.  
  1560. /*
  1561.  * File Open Stream Save
  1562.  */
  1563.  
  1564. FILE    *FOpenFile (char *name, char *mode)
  1565. {
  1566.     FILE    *fp;
  1567.  
  1568.     DISABLE_HARD_ERRORS;
  1569.     fp = fopen (name, mode);
  1570.     ENABLE_HARD_ERRORS;
  1571.  
  1572.     if (fp != (FILE *)NULL)
  1573.     {
  1574.         SaveFileDescriptor (fp);
  1575.     setvbuf (fp, (char *)NULL, _IOFBF, BUFSIZ);
  1576.     ChangeFileDescriptorStatus (fileno (fp), TRUE);
  1577.     }
  1578.  
  1579.     return fp;
  1580. }
  1581.  
  1582. /*
  1583.  * File D Open Stream Save
  1584.  */
  1585.  
  1586. FILE    *ReOpenFile (int fid, char *mode)
  1587. {
  1588.     FILE    *fp = fdopen (fid, mode);
  1589.  
  1590.     if (fp != (FILE *)NULL)
  1591.     {
  1592.         SaveFileDescriptor (fp);
  1593.     setvbuf (fp, (char *)NULL, _IOFBF, BUFSIZ);
  1594.     }
  1595.  
  1596.     return fp;
  1597. }
  1598.  
  1599. /*
  1600.  * File Close Stream Save
  1601.  */
  1602.  
  1603. int    CloseFile (FILE *fp)
  1604. {
  1605.     Word_B    *wb = e.OpenStreams;
  1606.     int        NEntries = WordBlockSize (wb);
  1607.     int        i;
  1608.  
  1609.     for (i = 0; i < NEntries; i++)
  1610.     {
  1611.         if (wb->w_words[i] == (char *)fp)
  1612.         wb->w_words[i] = (char *)NULL;
  1613.     }
  1614.  
  1615.     ChangeFileDescriptorStatus (fileno (fp), FALSE);
  1616.     return fclose (fp);
  1617. }
  1618.  
  1619. /*
  1620.  * Flush output streams
  1621.  */
  1622.  
  1623. void    FlushStreams (void)
  1624. {
  1625.     fflush (stdout);
  1626.     fflush (stderr);
  1627. }
  1628.  
  1629. /*
  1630.  * Save Descriptor in Environment
  1631.  */
  1632.  
  1633. static void F_LOCAL SaveFileDescriptor (FILE *fp)
  1634. {
  1635.     Word_B    *wb = e.OpenStreams;
  1636.     int        NEntries = WordBlockSize (wb);
  1637.     int        i;
  1638.  
  1639.     for (i = 0; i < NEntries; i++)
  1640.     {
  1641.         if (wb->w_words[i] == (char *)NULL)
  1642.     {
  1643.         wb->w_words[i] = (char *)fp;
  1644.         return;
  1645.     }
  1646.     }
  1647.  
  1648.     e.OpenStreams = AddWordToBlock ((char *)fp, wb);
  1649.     SetMemoryAreaNumber ((void *)e.OpenStreams, 0);
  1650. }
  1651.  
  1652. /*
  1653.  * Local Stat function to do some additional checks
  1654.  */
  1655.  
  1656. bool    S_stat (char *FileName, struct stat *Status)
  1657. {
  1658.     int        rc;
  1659.  
  1660.     CheckDOSFileName (FileName);
  1661.     DISABLE_HARD_ERRORS;
  1662.     rc = stat (FileName, Status);
  1663.     ENABLE_HARD_ERRORS;
  1664.  
  1665. /*
  1666.  * Watcom has a problem stat'ing the root directory!
  1667.  */
  1668.  
  1669. #if defined (__WATCOMC__)    /* WatCom C                */
  1670.     if (rc)
  1671.     {
  1672.     char    *fullpath;
  1673.     char    *cp;
  1674.  
  1675.     if ((fullpath = AllocateMemoryCell (FFNAME_MAX)) != (char *)NULL)
  1676.     {
  1677.         if (((cp = FindLastPathCharacter (strcpy (fullpath, FileName)))
  1678.              != (char *)NULL) &&
  1679.         ((strcmp (++cp, ParentDirLiteral) == 0) ||
  1680.          (strcmp (cp, CurrentDirLiteral) == 0)))
  1681.             strcat (cp, "/");
  1682.  
  1683. /* Generate the full path */
  1684.  
  1685.         GenerateFullExecutablePath (fullpath);
  1686.  
  1687. /* Root directory is a special case */
  1688.  
  1689.         if (strcmp (fullpath + 1, ":\\.") == 0)
  1690.         fullpath[3] = 0;
  1691.  
  1692.         DISABLE_HARD_ERRORS;
  1693.         rc = stat (fullpath, Status);
  1694.         ENABLE_HARD_ERRORS;
  1695.         ReleaseMemoryCell (fullpath);
  1696.     }
  1697.     }
  1698. #endif
  1699.  
  1700.     return rc ? FALSE : TRUE;
  1701. }
  1702.  
  1703. /*
  1704.  * Local access function to do some additional checks
  1705.  */
  1706.  
  1707. bool    S_access (char *FileName, int mode)
  1708. {
  1709.     int        rc;
  1710.  
  1711.     DISABLE_HARD_ERRORS;
  1712.     rc = access (FileName, mode);
  1713.     ENABLE_HARD_ERRORS;
  1714.  
  1715.     return rc ? FALSE : TRUE;
  1716. }
  1717.  
  1718. /*
  1719.  * Local change directory function to do some additional checks
  1720.  */
  1721.  
  1722. bool    S_chdir (char *PathName)
  1723. {
  1724.     int        rc;
  1725.  
  1726.     CheckDOSFileName (PathName);
  1727.  
  1728.     DISABLE_HARD_ERRORS;
  1729.     rc = chdir (PathName);
  1730.     ENABLE_HARD_ERRORS;
  1731.  
  1732.     return rc ? FALSE : TRUE;
  1733. }
  1734.  
  1735. /*
  1736.  * Local get current directory function to do some additional checks
  1737.  *
  1738.  * Assumes that PathName is a string of length PATH_MAX + 6.
  1739.  */
  1740.  
  1741. bool    S_getcwd (char *PathName, int drive)
  1742. {
  1743.     char        *res = (char *)NULL;
  1744. #if (OS_TYPE == OS_NT)
  1745.     unsigned int    cdrive;
  1746. #endif
  1747.  
  1748. #if defined (__TURBOC__)
  1749.     *(strcpy (PathName, RootDirectory)) = GetDriveLetter (drive);
  1750. #endif
  1751.  
  1752.     DISABLE_HARD_ERRORS;
  1753.  
  1754. #if (OS_TYPE != OS_UNIX)
  1755.     if (drive)
  1756.     {
  1757. #  if defined (__TURBOC__)
  1758.     res = getcurdir (drive, PathName + 3) ? (char *)NULL : PathName;
  1759. #  elif (OS_TYPE != OS_NT)
  1760.     res = _getdcwd (drive, PathName, PATH_MAX + 4);
  1761. #  else
  1762.     cdrive = GetCurrentDrive ();
  1763.  
  1764.     if (SetCurrentDrive (drive) != -1)
  1765.     {
  1766.         res = !GetCurrentDirectory (PATH_MAX + 4, PathName) ? (char *)NULL
  1767.                                     : PathName;
  1768.         SetCurrentDrive (cdrive);
  1769.     }
  1770. #  endif
  1771.     }
  1772.  
  1773.     else
  1774. #  if defined (__EMX__)
  1775.     res = _getdcwd (GetCurrentDrive (), PathName, PATH_MAX + 4);
  1776. #  else
  1777.     res = getcwd (PathName, PATH_MAX + 4);
  1778. #  endif
  1779. #else
  1780.     res = getcwd (PathName, PATH_MAX + 4);
  1781. #endif
  1782.  
  1783.     ENABLE_HARD_ERRORS;
  1784.     PathName[PATH_MAX + 5] = 0;
  1785.  
  1786. /* Convert to Unix format */
  1787.  
  1788.     PATH_TO_UNIX (PathName);
  1789.     PATH_TO_LOWER_CASE (PathName);
  1790.  
  1791. /* Drive letter in lower case */
  1792.  
  1793. #if (OS_TYPE != OS_UNIX)
  1794.     *PathName = (char)tolower (*PathName);
  1795. #endif
  1796.  
  1797.     return (res == (char *)NULL) ? FALSE : TRUE;
  1798. }
  1799.  
  1800. /*
  1801.  * Open the directory stream
  1802.  *
  1803.  *
  1804.  * OS/2 version
  1805.  */
  1806.  
  1807. #if (OS_TYPE == OS_OS2)
  1808. DIR * _CDECL    opendir (name)
  1809. const char    *name;
  1810. {
  1811.     DIR            *dirp;
  1812.     char        *last;
  1813.     DIRCONT        *dp;
  1814.     char        *nbuf;
  1815.     HDIR        d_handle = HDIR_SYSTEM;
  1816.     bool        HPFS;
  1817.     int            len = strlen (name);
  1818.     unsigned long    rc;
  1819.  
  1820. #  if (OS_SIZE == OS_32)
  1821.     FILEFINDBUF3     dtabuf;
  1822. #    define DTA_NAME    dtabuf.achName
  1823.     ULONG        d_count = 1;
  1824. #  else
  1825.     FILEFINDBUF     dtabuf;
  1826. #    define DTA_NAME    dtabuf.achName
  1827.     USHORT        d_count = 1;
  1828. #  endif
  1829.  
  1830.     if (!len)
  1831.     {
  1832.     errno = ENOTDIR;
  1833.     return (DIR *)NULL;
  1834.     }
  1835.  
  1836.     if ((nbuf = AllocateMemoryCell (len + 5)) == (char *)NULL)
  1837.     return (DIR *) NULL;
  1838.  
  1839.     strcpy (nbuf, name);
  1840.     last = &nbuf[len - 1];
  1841.  
  1842. /* Ok, DOS is very picky about its directory names.  The following are
  1843.  * valid.
  1844.  *
  1845.  *  c:/
  1846.  *  c:.
  1847.  *  c:name/name1
  1848.  *
  1849.  *  c:name/ is not valid
  1850.  */
  1851.  
  1852.     if (((*last == CHAR_DOS_PATH) || IsPathCharacter (*last)) &&
  1853.     (len > 1) && (!((len == 3) && IsDriveCharacter (name[1]))))
  1854.     *(last--) = 0;
  1855.  
  1856. /* Check its a directory and get some space */
  1857.  
  1858.     if ((!IsDirectory (nbuf)) ||
  1859.         ((dirp = (DIR *) AllocateMemoryCell (sizeof (DIR))) == (DIR *) NULL))
  1860.     {
  1861.     ReleaseMemoryCell (nbuf);
  1862.     return (DIR *)NULL;
  1863.     }
  1864.  
  1865. /* Set up to find everything */
  1866.  
  1867.     if ((*last != CHAR_DOS_PATH) && !IsPathCharacter (*last))
  1868.     strcat (last, DirectorySeparator);
  1869.  
  1870.     strcat (last, "*.*");
  1871.  
  1872. /* For OS/2, find the file system type */
  1873.  
  1874.     HPFS = IsHPFSFileSystem (nbuf);
  1875.  
  1876.     dirp->dd_loc      = 0;
  1877.     dirp->dd_cp       = (DIRCONT *) NULL;
  1878.     dirp->dd_contents = (DIRCONT *) NULL;
  1879.  
  1880.     DISABLE_HARD_ERRORS;
  1881.  
  1882. #  if (OS_SIZE == OS_32)
  1883.     rc = DosFindFirst (nbuf, &d_handle, OS_FILE_ATTRIBUTES, &dtabuf,
  1884.                sizeof (FILEFINDBUF3), &d_count, FIL_STANDARD);
  1885. #  else
  1886.     rc = DosFindFirst (nbuf, &d_handle, OS_FILE_ATTRIBUTES, &dtabuf,
  1887.                sizeof (FILEFINDBUF), &d_count, (ULONG)0);
  1888. #  endif
  1889.  
  1890.     ENABLE_HARD_ERRORS;
  1891.  
  1892.     if (rc)
  1893.     {
  1894.     ReleaseMemoryCell (nbuf);
  1895.     ReleaseMemoryCell (dirp);
  1896.     return (DIR *) NULL;
  1897.     }
  1898.  
  1899. /* Wander through the directory! */
  1900.  
  1901.     do
  1902.     {
  1903.     if (((dp = (DIRCONT *) AllocateMemoryCell (sizeof (DIRCONT)))
  1904.          == (DIRCONT *)NULL) ||
  1905.         ((dp->_d_entry = StringCopy (DTA_NAME)) == null))
  1906.     {
  1907.         if (dp->_d_entry != (char *)NULL)
  1908.         ReleaseMemoryCell ((char *)dp);
  1909.  
  1910.         ReleaseMemoryCell (nbuf);
  1911.         FreeDirectoryListing (dirp->dd_contents);
  1912.  
  1913.         DosFindClose (d_handle);
  1914.         return (DIR *) NULL;
  1915.     }
  1916.  
  1917.     if (!HPFS)
  1918.         strlwr (dp->_d_entry);
  1919.  
  1920.     if (dirp->dd_contents != (DIRCONT *) NULL)
  1921.         dirp->dd_cp = dirp->dd_cp->_d_next = dp;
  1922.  
  1923.     else
  1924.         dirp->dd_contents = dirp->dd_cp = dp;
  1925.  
  1926.     dp->_d_next = (DIRCONT *) NULL;
  1927.  
  1928.     d_count = 1;
  1929.     } while (DosFindNext (d_handle, &dtabuf, sizeof (FILEFINDBUF),
  1930.               &d_count) == 0);
  1931.  
  1932.     dirp->dd_cp = dirp->dd_contents;
  1933.     ReleaseMemoryCell (nbuf);
  1934.  
  1935.     DosFindClose (d_handle);
  1936.     return dirp;
  1937. }
  1938. #endif
  1939.  
  1940. /*
  1941.  * NT version
  1942.  */
  1943.  
  1944. #if (OS_TYPE == OS_NT)
  1945. DIR * _CDECL    opendir (name)
  1946. const char    *name;
  1947. {
  1948.     DIR            *dirp;
  1949.     char        *last;
  1950.     DIRCONT        *dp;
  1951.     char        *nbuf;
  1952.     HANDLE        d_handle;
  1953.     bool        HPFS;
  1954.     int            len = strlen (name);
  1955.     WIN32_FIND_DATA     dtabuf;
  1956.  
  1957.     if (!len)
  1958.     {
  1959.     errno = ENOTDIR;
  1960.     return (DIR *)NULL;
  1961.     }
  1962.  
  1963.     if ((nbuf = AllocateMemoryCell (len + 5)) == (char *)NULL)
  1964.     return (DIR *) NULL;
  1965.  
  1966.     strcpy (nbuf, name);
  1967.     last = &nbuf[len - 1];
  1968.  
  1969. /* Ok, DOS is very picky about its directory names.  The following are
  1970.  * valid.
  1971.  *
  1972.  *  c:/
  1973.  *  c:.
  1974.  *  c:name/name1
  1975.  *
  1976.  *  c:name/ is not valid
  1977.  */
  1978.  
  1979.     if (((*last == CHAR_DOS_PATH) || IsPathCharacter (*last)) &&
  1980.     (len > 1) && (!((len == 3) && IsDriveCharacter (name[1]))))
  1981.     *(last--) = 0;
  1982.  
  1983. /* Check its a directory and get some space */
  1984.  
  1985.     if ((!IsDirectory (nbuf)) ||
  1986.         ((dirp = (DIR *) AllocateMemoryCell (sizeof (DIR))) == (DIR *) NULL))
  1987.     {
  1988.     ReleaseMemoryCell (nbuf);
  1989.     return (DIR *)NULL;
  1990.     }
  1991.  
  1992. /* Set up to find everything */
  1993.  
  1994.     if ((*last != CHAR_DOS_PATH) && !IsPathCharacter (*last))
  1995.     strcat (last, DirectorySeparator);
  1996.  
  1997.     strcat (last, "*.*");
  1998.  
  1999. /* For OS/2, find the file system type */
  2000.  
  2001.     HPFS = IsHPFSFileSystem (nbuf);
  2002.  
  2003.     dirp->dd_loc      = 0;
  2004.     dirp->dd_cp       = (DIRCONT *) NULL;
  2005.     dirp->dd_contents = (DIRCONT *) NULL;
  2006.  
  2007.     DISABLE_HARD_ERRORS;
  2008.     d_handle = FindFirstFile (nbuf, &dtabuf);
  2009.     ENABLE_HARD_ERRORS;
  2010.  
  2011.     if (d_handle == INVALID_HANDLE_VALUE)
  2012.     {
  2013.     ReleaseMemoryCell (nbuf);
  2014.     ReleaseMemoryCell (dirp);
  2015.     return (DIR *) NULL;
  2016.     }
  2017.  
  2018. /* Wander through the directory! */
  2019.  
  2020.     do
  2021.     {
  2022.     if (((dp = (DIRCONT *) AllocateMemoryCell (sizeof (DIRCONT)))
  2023.          == (DIRCONT *)NULL) ||
  2024.         ((dp->_d_entry = StringCopy (dtabuf.cFileName)) == null))
  2025.     {
  2026.         if (dp->_d_entry != (char *)NULL)
  2027.         ReleaseMemoryCell ((char *)dp);
  2028.  
  2029.         ReleaseMemoryCell (nbuf);
  2030.         FreeDirectoryListing (dirp->dd_contents);
  2031.  
  2032.         FindClose (d_handle);
  2033.         return (DIR *) NULL;
  2034.     }
  2035.  
  2036.     if (!HPFS)
  2037.         strlwr (dp->_d_entry);
  2038.  
  2039.     if (dirp->dd_contents != (DIRCONT *) NULL)
  2040.         dirp->dd_cp = dirp->dd_cp->_d_next = dp;
  2041.  
  2042.     else
  2043.         dirp->dd_contents = dirp->dd_cp = dp;
  2044.  
  2045.     dp->_d_next = (DIRCONT *) NULL;
  2046.  
  2047.     } while (FindNextFile (d_handle, &dtabuf));
  2048.  
  2049.     dirp->dd_cp = dirp->dd_contents;
  2050.     ReleaseMemoryCell (nbuf);
  2051.  
  2052.     FindClose (d_handle);
  2053.     return dirp;
  2054. }
  2055. #endif
  2056.  
  2057. /*
  2058.  * DOS Version
  2059.  */
  2060.  
  2061. #if (OS_TYPE == OS_DOS)
  2062. DIR * _CDECL    opendir (name)
  2063. const char    *name;
  2064. {
  2065.     DIR            *dirp;
  2066.     char        *last;
  2067.     DIRCONT        *dp;
  2068.     char        *nbuf;
  2069.     int            len = strlen (name);
  2070.     unsigned long    rc;
  2071.  
  2072. #  if defined (__TURBOC__)
  2073.     struct ffblk            dtabuf;
  2074. #    define DTA_NAME            dtabuf.ff_name
  2075. #    define DIR_FINDNEXT(a)        findnext (a)
  2076. #    define DIR_FINDFIRST(F,A,B)    findfirst (F, B, A)
  2077. #  elif defined (__EMX__)
  2078.     struct _find            dtabuf;
  2079. #    define DTA_NAME            dtabuf.name
  2080. #    define DIR_FINDNEXT(a)        __findnext (a)
  2081. #    define DIR_FINDFIRST(F,A,B)    __findfirst (F, A, B)
  2082. #  else
  2083.     struct find_t            dtabuf;
  2084. #    define DTA_NAME            dtabuf.name
  2085. #    define DIR_FINDNEXT(a)        _dos_findnext (a)
  2086. #    define DIR_FINDFIRST(F,A,B)    _dos_findfirst (F, A, B)
  2087. #  endif
  2088.  
  2089.     if (!len)
  2090.     {
  2091.     errno = ENOTDIR;
  2092.     return (DIR *)NULL;
  2093.     }
  2094.  
  2095.     if ((nbuf = AllocateMemoryCell (len + 5)) == (char *)NULL)
  2096.     return (DIR *) NULL;
  2097.  
  2098.     strcpy (nbuf, name);
  2099.     last = &nbuf[len - 1];
  2100.  
  2101. /* Ok, DOS is very picky about its directory names.  The following are
  2102.  * valid.
  2103.  *
  2104.  *  c:/
  2105.  *  c:.
  2106.  *  c:name/name1
  2107.  *
  2108.  *  c:name/ is not valid
  2109.  */
  2110.  
  2111.     if (((*last == CHAR_DOS_PATH) || IsPathCharacter (*last)) &&
  2112.     (len > 1) && (!((len == 3) && IsDriveCharacter (name[1]))))
  2113.     *(last--) = 0;
  2114.  
  2115. /* Check its a directory and get some space */
  2116.  
  2117.     if ((!IsDirectory (nbuf)) ||
  2118.         ((dirp = (DIR *) AllocateMemoryCell (sizeof (DIR))) == (DIR *) NULL))
  2119.     {
  2120.     ReleaseMemoryCell (nbuf);
  2121.     return (DIR *)NULL;
  2122.     }
  2123.  
  2124. /* Set up to find everything */
  2125.  
  2126.     if ((*last != CHAR_DOS_PATH) && !IsPathCharacter (*last))
  2127.     strcat (last, DirectorySeparator);
  2128.  
  2129.     strcat (last, "*.*");
  2130.  
  2131.     dirp->dd_loc      = 0;
  2132.     dirp->dd_cp       = (DIRCONT *) NULL;
  2133.     dirp->dd_contents = (DIRCONT *) NULL;
  2134.  
  2135.     DISABLE_HARD_ERRORS;
  2136.  
  2137.     rc = DIR_FINDFIRST (nbuf, OS_FILE_ATTRIBUTES, &dtabuf);
  2138.  
  2139.     ENABLE_HARD_ERRORS;
  2140.  
  2141.     if (rc)
  2142.     {
  2143.     ReleaseMemoryCell (nbuf);
  2144.     ReleaseMemoryCell (dirp);
  2145.     return (DIR *) NULL;
  2146.     }
  2147.  
  2148.     do
  2149.     {
  2150.     if (((dp = (DIRCONT *) AllocateMemoryCell (sizeof (DIRCONT)))
  2151.          == (DIRCONT *)NULL) ||
  2152.         ((dp->_d_entry = StringCopy (DTA_NAME)) == null))
  2153.     {
  2154.         if (dp->_d_entry != (char *)NULL)
  2155.         ReleaseMemoryCell ((char *)dp);
  2156.  
  2157.         ReleaseMemoryCell (nbuf);
  2158.         FreeDirectoryListing (dirp->dd_contents);
  2159.         return (DIR *) NULL;
  2160.     }
  2161.  
  2162.     strlwr (dp->_d_entry);
  2163.  
  2164.     if (dirp->dd_contents != (DIRCONT *) NULL)
  2165.         dirp->dd_cp = dirp->dd_cp->_d_next = dp;
  2166.  
  2167.     else
  2168.         dirp->dd_contents = dirp->dd_cp = dp;
  2169.  
  2170.     dp->_d_next = (DIRCONT *) NULL;
  2171.  
  2172.     } while (DIR_FINDNEXT (&dtabuf) == 0);
  2173.  
  2174.     dirp->dd_cp = dirp->dd_contents;
  2175.     ReleaseMemoryCell (nbuf);
  2176.     return dirp;
  2177. }
  2178. #endif
  2179. /*
  2180.  * Close the directory stream
  2181.  */
  2182.  
  2183. #if (OS_TYPE != OS_UNIX)
  2184. int _CDECL    closedir (dirp)
  2185. DIR        *dirp;
  2186. {
  2187.     if (dirp != (DIR *)NULL)
  2188.     {
  2189.     FreeDirectoryListing (dirp->dd_contents);
  2190.     ReleaseMemoryCell ((char *)dirp);
  2191.     }
  2192.  
  2193.     return 0;
  2194. }
  2195.  
  2196. /*
  2197.  * Read the next record from the stream
  2198.  */
  2199.  
  2200. struct dirent * _CDECL readdir (dirp)
  2201. DIR        *dirp;
  2202. {
  2203.     static struct dirent    dp;
  2204.  
  2205.     if ((dirp == (DIR *)NULL) || (dirp->dd_cp == (DIRCONT *) NULL))
  2206.     return (struct dirent *) NULL;
  2207.  
  2208.     dp.d_reclen = strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry));
  2209.     dp.d_off    = dirp->dd_loc * 32;
  2210.     dp.d_ino    = (ino_t)++dirp->dd_loc;
  2211.     dirp->dd_cp = dirp->dd_cp->_d_next;
  2212.  
  2213.     return &dp;
  2214. }
  2215.  
  2216. /*
  2217.  * Release the internal structure
  2218.  */
  2219.  
  2220. static void    FreeDirectoryListing (dp)
  2221. DIRCONT        *dp;
  2222. {
  2223.     DIRCONT    *odp;
  2224.  
  2225.     while ((odp = dp) != (DIRCONT *)NULL)
  2226.     {
  2227.     if (dp->_d_entry != (char *)NULL)
  2228.         ReleaseMemoryCell (dp->_d_entry);
  2229.  
  2230.     dp = dp->_d_next;
  2231.     ReleaseMemoryCell ((char *)odp);
  2232.     }
  2233. }
  2234. #endif
  2235.  
  2236. /*
  2237.  * For OS/2 and NT, we need to know if we have to convert to lower case.  This
  2238.  * only applies to non-HPFS (FAT, NETWARE etc) file systems.
  2239.  */
  2240.  
  2241. #if (OS_TYPE == OS_OS2) 
  2242. /*
  2243.  * Define the know FAT systems
  2244.  */
  2245.  
  2246. static char    *FATSystems[] = {"FAT", "NETWARE", (char *)NULL};
  2247.  
  2248. /*
  2249.  * Check for Long filenames
  2250.  */
  2251.  
  2252. bool        IsHPFSFileSystem (char *directory)
  2253. {
  2254.     BYTE        bData[128];
  2255.     BYTE        bName[3];
  2256.     int            i;
  2257.     char        *FName;
  2258.     unsigned long    rc;
  2259.     OSCALL_PARAM    cbData;
  2260.     unsigned int    nDrive;
  2261. #  if (OS_SIZE == OS_32)
  2262.     PFSQBUFFER2        pFSQ = (PFSQBUFFER2)bData;
  2263. #  endif
  2264.  
  2265. /*
  2266.  * Mike tells me there are IFS calls to determine this, but he carn't
  2267.  * remember which.  So we read the partition info and check for HPFS.
  2268.  */
  2269.  
  2270.     if (isalpha (directory[0]) && IsDriveCharacter (directory[1]))
  2271.     nDrive = toupper (directory[0]) - '@';
  2272.  
  2273.     else
  2274.     nDrive = GetCurrentDrive ();
  2275.  
  2276. /* Set up the drive name */
  2277.  
  2278.     bName[0] = (char) (nDrive + '@');
  2279.     bName[1] = CHAR_DRIVE;
  2280.     bName[2] = 0;
  2281.  
  2282.     cbData = sizeof (bData);
  2283.  
  2284. /* Read the info, if we fail - assume non-HPFS */
  2285.  
  2286.     DISABLE_HARD_ERRORS;
  2287.  
  2288. #  if (OS_SIZE == OS_32)
  2289.     rc = DosQFSAttach (bName, 0, FSAIL_QUERYNAME, pFSQ, &cbData);
  2290. #  else
  2291.     rc = DosQFSAttach (bName, 0, FSAIL_QUERYNAME, bData, &cbData, 0L);
  2292. #  endif
  2293.  
  2294.     ENABLE_HARD_ERRORS;
  2295.  
  2296.     if (rc)
  2297.     return FALSE;
  2298.  
  2299. #  if (OS_SIZE == OS_32)
  2300.     FName = pFSQ->szName + pFSQ->cbName + 1;
  2301. #  else
  2302.     FName = bData + (*((USHORT *) (bData + 2)) + 7);
  2303. #  endif
  2304.  
  2305.     for (i = 0; FATSystems[i] != (char *)NULL; i++)
  2306.     {
  2307.         if (stricmp (FName, FATSystems[i]) == 0)
  2308.         return FALSE;
  2309.     }
  2310.  
  2311.     return TRUE;
  2312. }
  2313. #endif
  2314.  
  2315. /*
  2316.  * Windows NT version
  2317.  */
  2318.  
  2319. #if (OS_TYPE == OS_NT) 
  2320. bool        IsHPFSFileSystem (char *directory)
  2321. {
  2322.     char        bName[4];
  2323.     DWORD        flags;
  2324.     DWORD        maxname;
  2325.     BOOL        rc;
  2326.     unsigned int    nDrive;
  2327.  
  2328. /*
  2329.  * Mike tells me there are IFS calls to determine this, but he carn't
  2330.  * remember which.  So we read the partition info and check for HPFS.
  2331.  */
  2332.  
  2333.     if (isalpha (directory[0]) && IsDriveCharacter (directory[1]))
  2334.     nDrive = toupper (directory[0]) - '@';
  2335.  
  2336.     else
  2337.     nDrive = GetCurrentDrive ();
  2338.  
  2339. /* Set up the drive name */
  2340.  
  2341.     bName[0] = (char) (nDrive + '@');
  2342.     bName[1] = CHAR_DRIVE;
  2343.     bName[2] = CHAR_DOS_PATH;
  2344.     bName[3] = 0;
  2345.  
  2346. /* Read the volume info, if we fail - assume non-HPFS */
  2347.  
  2348.     DISABLE_HARD_ERRORS;
  2349.  
  2350.     rc = GetVolumeInformation (bName, (LPTSTR)NULL, 0, (LPDWORD)NULL,
  2351.                    &maxname, &flags, (LPTSTR)NULL, 0);
  2352.     ENABLE_HARD_ERRORS;
  2353.  
  2354.     return C2bool ((rc) && (flags & FS_CASE_SENSITIVE));
  2355. }
  2356. #endif
  2357.  
  2358.  
  2359. /*
  2360.  * Get directory on drive x
  2361.  */
  2362.  
  2363. #if defined (__WATCOMC__)
  2364. char    *_getdcwd (int drive, char *PathName, int len)
  2365. {
  2366.     char        TPath [PATH_MAX + 3];
  2367. #  if (OS_TYPE == OS_DOS)
  2368.     union REGS        r;
  2369.  
  2370. /* This is really a bit of a cheat.  I'm not sure why it works, but is
  2371.  * does.  The missing bit is that you really should set the DS register up
  2372.  * but that causes a memory violation. SO!!
  2373.  */
  2374.  
  2375.     r.x.REG_AX = 0x4700;
  2376.     r.h.dl = drive;
  2377.     r.x.esi = FP_OFF (TPath);
  2378.  
  2379.     DosInterrupt (&r, &r);
  2380.  
  2381.     if (r.x.cflag & INTR_CF)
  2382.     return (char *)NULL;
  2383.     
  2384. #  else
  2385.     ULONG    cbBuf = PATH_MAX + 3;
  2386.  
  2387.     if (DosQueryCurrentDir (drive, TPath, &cbBuf))
  2388.     return (char *)NULL;
  2389. #  endif
  2390.  
  2391. /* Insert the drive and root directory */
  2392.  
  2393.     *(strcpy (PathName, RootDirectory)) = GetDriveLetter (drive);
  2394.     return strcat (PathName, TPath);
  2395. }
  2396. #endif
  2397.  
  2398. #if defined (__EMX__) 
  2399. char    *_getdcwd (int drive, char *PathName, int len)
  2400. {
  2401.     char        TPath [PATH_MAX + 3];
  2402.  
  2403.     if (_getcwd1 (TPath, drive + 'A' - 1) != 0)
  2404.     return (char *)NULL;
  2405.  
  2406. /* Insert the drive and root directory */
  2407.  
  2408.     *(strcpy (PathName, RootDirectory)) = GetDriveLetter (drive);
  2409.     return strcpy (PathName + 2, TPath);
  2410. }
  2411. #endif
  2412.  
  2413. /*
  2414.  *  MODULE ABSTRACT: _setargv
  2415.  *
  2416.  *  UNIX like command line expansion
  2417.  */
  2418.  
  2419. /*
  2420.  * OS/2 2.x (32-bit) version
  2421.  */
  2422.  
  2423. #if (OS_TYPE == OS_OS2)
  2424. #  if (OS_SIZE == OS_32)
  2425. void    ENTRY_POINT (void)
  2426. {
  2427.     APIRET    rc;
  2428.     PTIB    ptib;
  2429.     PPIB    ppib;
  2430.     char    *MName;
  2431.     Word_B    *Alist = (Word_B *)NULL;
  2432.  
  2433. /* Get the command line and program name */
  2434.  
  2435.     if ((rc = DosGetInfoBlocks (&ptib, &ppib)))
  2436.         PrintErrorMessage ("DosGetInfoBlocks: Cannot find command line\n%s\n",
  2437.                GetOSSystemErrorMessage (rc));
  2438.  
  2439.     if ((MName = GetAllocatedSpace (FFNAME_MAX)) == (char *)NULL)
  2440.     PrintErrorMessage (Outofmemory1);
  2441.  
  2442.     if ((rc = DosQueryModuleName (ppib->pib_hmte, FFNAME_MAX - 1, MName)))
  2443.         PrintErrorMessage ("DosQueryModuleName: Cannot get program name\n%s\n",
  2444.                GetOSSystemErrorMessage (rc));
  2445.  
  2446. /* Save the program name and process the command line */
  2447.  
  2448.     _APgmName = MName;
  2449.     _Ex_ProcessEMXArguments (ppib->pib_pchcmd, &Alist);
  2450.  
  2451. /* Terminate */
  2452.  
  2453.     _Ex_SaveArgvValue ((char *)NULL, FALSE, &Alist);
  2454. }
  2455.  
  2456. #  else /* (OS_SIZE == OS_16) */
  2457.  
  2458. /*
  2459.  * OS/2 1.x (16-bit) version
  2460.  */
  2461.  
  2462. void    ENTRY_POINT (void)
  2463. {
  2464.     char far        *argvp = (char far *)((((long)_aenvseg) << 16));
  2465.     ushort        off = _acmdln;
  2466.     Word_B        *Alist = (Word_B *)NULL;
  2467.  
  2468.     while (--off)
  2469.     {
  2470.     if (argvp[off - 1] == 0)
  2471.          break;
  2472.     }
  2473.  
  2474. /* Add program name */
  2475.  
  2476.     _APgmName =  &argvp[off];
  2477.  
  2478.     if (argvp[_acmdln] == 0)
  2479.     _Ex_SaveArgvValue (_APgmName, TRUE, &Alist);
  2480.  
  2481.     else
  2482.     {
  2483.     argvp += _acmdln;
  2484.     _Ex_ProcessEMXArguments (argvp, &Alist);
  2485.     }
  2486.  
  2487. /* Terminate */
  2488.  
  2489.     _Ex_SaveArgvValue ((char *)NULL, FALSE, &Alist);
  2490. }
  2491. #  endif
  2492.  
  2493. #elif (OS_TYPE == OS_DOS)
  2494.  
  2495. /*
  2496.  * MSDOS version
  2497.  */
  2498.  
  2499. void    ENTRY_POINT (void)
  2500. {
  2501.     Word_B        *Alist = (Word_B *)NULL;
  2502.     char        *s;        /* Temporary string pointer        */
  2503. #  if (OS_SIZE == OS_16)
  2504.                     /* Set up pointer to command line */
  2505.     unsigned int    envs = *(int far *)((((long)_psp) << 16) + 0x02cL);
  2506.     union REGS        r;
  2507.     unsigned int    Length;
  2508.  
  2509. /* For reasons that escape me, MSC 6.0 does sets up _osmajor and _osminor
  2510.  * in the command line parser!
  2511.  */
  2512.  
  2513.     r.h.ah = 0x30;
  2514.     DosInterrupt (&r, &r);
  2515.     _osminor = r.h.ah;
  2516.     _osmajor = r.h.al;
  2517.  
  2518. /* Check the length */
  2519.  
  2520.     _ACmdLine = (char *)((((long)_psp) << 16) + 0x080L);
  2521.  
  2522.     if ((Length = (unsigned int)*(_ACmdLine++)) > 127)
  2523.     Length = 127;
  2524.  
  2525.     _ACmdLine[Length] = 0;
  2526.  
  2527. /* Command line can be null or 0x0d terminated - convert to null */
  2528.  
  2529.     if ((s = strchr (_ACmdLine, CHAR_RETURN)) != (char *)NULL)
  2530.     *s = 0;
  2531.  
  2532. /* Get the program name */
  2533.  
  2534.     if ((_osmajor <= 2) || (envs == 0))
  2535.     s = "unknown";
  2536.  
  2537. /* In the case of DOS 3+, we look in the environment space */
  2538.  
  2539.     else
  2540.     {
  2541.     s = (char far *)(((long)envs) << 16);
  2542.  
  2543.     while (*s)
  2544.     {
  2545.         while (*(s++) != 0)
  2546.         continue;
  2547.     }
  2548.  
  2549.     s += 3;
  2550.     }
  2551.  
  2552. #  elif defined (__WATCOMC__)
  2553.     _ACmdLine = _LpCmdLine;
  2554.     s = _LpPgmName;
  2555. #  endif
  2556.  
  2557. /* Add the program name        */
  2558.  
  2559.     _APgmName = s;
  2560.     _Ex_SaveArgvValue (s, TRUE, &Alist);
  2561.     _Ex_CommandLine (_ACmdLine, &Alist);
  2562.     _Ex_SaveArgvValue ((char *)NULL, FALSE, &Alist);
  2563. }
  2564.  
  2565. #elif (OS_TYPE == OS_NT)
  2566.  
  2567. /* NT version */
  2568.  
  2569. void    ENTRY_POINT (void)
  2570. {
  2571.     char    *MName;
  2572.     Word_B    *Alist = (Word_B *)NULL;
  2573.  
  2574.     _ACmdLine = GetCommandLine ();
  2575.  
  2576. /* Get the command line and program name */
  2577.  
  2578.     if ((MName = GetAllocatedSpace (MAX_PATH)) == (char *)NULL)
  2579.     PrintErrorMessage (Outofmemory1);
  2580.  
  2581.     if (!GetModuleFileName (0, MName, MAX_PATH))
  2582.     PrintErrorMessage ("GetModuleFileName: Cannot get program name\n%s\n",
  2583.                GetOSSystemErrorMessage (GetLastError ()));
  2584.  
  2585. /* Save the program name and process the command line */
  2586.  
  2587.     _APgmName = MName;
  2588.  
  2589.     if (*_ACmdLine)
  2590.     _Ex_CommandLine (_ACmdLine, &Alist);
  2591.     
  2592.     else
  2593.     _Ex_SaveArgvValue (MName, TRUE, &Alist);
  2594.  
  2595.     _Ex_SaveArgvValue ((char *)NULL, FALSE, &Alist);
  2596. }
  2597. #endif
  2598.  
  2599. /*
  2600.  * A fix for EMX, so that startup gets the arguments, I hope.
  2601.  *
  2602.  * Initialize the C run-time library.  This function is called from crt0.s.
  2603.  * We know argc and argv are on the stack, and crt0.s does not remove them
  2604.  * from the start before calling main, so we just replace the values, I
  2605.  * hope.
  2606.  */
  2607.  
  2608. #if defined (__EMX__)
  2609. void    _startup (int argc, char **argv)
  2610. {
  2611.     int        i, j;
  2612.  
  2613. /*
  2614.  * Print a warning message on handle 2 (stderr) if emx.dll or
  2615.  * emx.exe is out of date.
  2616.  */
  2617.  
  2618.     if (_emx_vcmp < 0x302e3868)   /* 0.8h */
  2619.     __write (2, _version_warning, strlen (_version_warning));
  2620. /*
  2621.  * Fix the stack
  2622.  */
  2623.  
  2624. #  if defined (EMX_DOS)
  2625.    for (i = 0; i < argc; i++)
  2626.    {
  2627.        __write (2, "arg = <", 7);
  2628.        __write (2, argv[i], strlen (argv[i]));
  2629.        __write (2, ">\n", 2);
  2630.    }
  2631. #  else
  2632.     GetArgcV ();
  2633.     argc = ARG_COUNT;
  2634.     argv = ARG_ARRAY;
  2635. #  endif
  2636.  
  2637. /* Initialize the file handles and the streams. */
  2638.  
  2639.     for (i = 0; i < _nfiles; ++i)
  2640.     {
  2641.     _files[i] = 0;
  2642.     _lookahead[i] = -1;
  2643.     _streamv[i].flags = 0;
  2644.  
  2645. /*
  2646.  * Get the handle type.  If this fails, the handle is not open.
  2647.  */
  2648.  
  2649.     if (ioctl (i, FGETHTYPE, &j) >= 0)
  2650.     {
  2651.         _files[i] |= O_TEXT;
  2652.  
  2653.         if (HT_ISDEV (j))
  2654.         _files[i] |= F_DEV;
  2655.  
  2656.         if (j == HT_NPIPE)
  2657.         _files[i] |= F_NPIPE;
  2658.  
  2659.         _streamv[i].flags |= _IOOPEN;
  2660.         _streamv[i].ptr = NULL;
  2661.         _streamv[i].buffer = NULL;
  2662.         _streamv[i].rcount = 0;
  2663.         _streamv[i].wcount = 0;
  2664.         _streamv[i].handle = i;
  2665.         _streamv[i].buf_size = 0;
  2666.         _streamv[i].tmpidx = 0;
  2667.         _streamv[i].pid = 0;
  2668.         _streamv[i].flush = _flushstream;
  2669.  
  2670.         switch (i)
  2671.         {
  2672.  
  2673. /*
  2674.  * stdin is always buffered.
  2675.  */
  2676.  
  2677.         case 0:
  2678.             _files[0] |= O_RDONLY;
  2679.             _streamv[0].flags |= _IOREAD | _IOFBF | _IOBUFUSER;
  2680.             _streamv[0].ptr = ibuf;
  2681.             _streamv[0].buffer = ibuf;
  2682.             _streamv[0].buf_size = BUFSIZ;
  2683.             break;
  2684.  
  2685. /*
  2686.  * stdout is buffered unless it's connected to a device.
  2687.  */
  2688.         case 1:            
  2689.             _files[i] |= O_WRONLY;
  2690.             _streamv[i].flags |= (_IOWRT | _IOBUFNONE |
  2691.                           (HT_ISDEV (j) ? _IONBF : _IOFBF));
  2692.             break;
  2693.  
  2694. /*
  2695.  * stderr is always unbuffered.
  2696.  */
  2697.         case 2:
  2698.             _files[i] |= O_WRONLY;
  2699.             _streamv[i].flags |= _IOWRT | _IOBUFNONE | _IONBF;
  2700.             break;
  2701.  
  2702. /*
  2703.  * All other handles can be read and written.  A handle is buffered
  2704.  * unless it's connected to a device.
  2705.  */
  2706.  
  2707.         default:
  2708.             _files[i] |= O_RDWR;
  2709.             _streamv[i].flags |= (_IORW | _IOBUFNONE |
  2710.                         (HT_ISDEV (j) ? _IONBF : _IOFBF));
  2711.             break;
  2712.         }
  2713.     }
  2714.     }
  2715. }
  2716. #endif
  2717. /*
  2718.  * Expand the DOS Command line
  2719.  */
  2720.  
  2721. #if (OS_TYPE != OS_UNIX)
  2722. static void F_LOCAL _Ex_CommandLine (char *argvp, Word_B **Alist)
  2723. {
  2724.     char    *spos;            /* End of string pointer    */
  2725.     char    *cpos;            /* Start of string pointer    */
  2726.     char    *tpos;
  2727.     char    *fn;            /* Extracted file name string    */
  2728.  
  2729. /* Search for next separator */
  2730.  
  2731.     spos = argvp;
  2732.  
  2733.     while (*(cpos = _Ex_SkipWhiteSpace (spos)))
  2734.     {
  2735.  
  2736. /* Extract string argument */
  2737.  
  2738.     if ((*cpos == CHAR_DOUBLE_QUOTE) || (*cpos == CHAR_SINGLE_QUOTE))
  2739.     {
  2740.         spos = cpos + 1;
  2741.  
  2742.         do
  2743.         {
  2744.         if ((spos = strchr (spos, *cpos)) != NULL)
  2745.         {
  2746.             spos++;
  2747.             if (spos[-2] != CHAR_META)
  2748.             break;
  2749.         }
  2750.  
  2751.         else
  2752.             spos = &spos[strlen (cpos)];
  2753.  
  2754.         } while (*spos);
  2755.  
  2756.         fn    = _Ex_GetSpace (spos - cpos - 2, cpos + 1);
  2757.  
  2758. /* Remove escaped characters */
  2759.  
  2760.        tpos = fn;
  2761.  
  2762.        while ((tpos = strchr (tpos, *cpos)) != (char *)NULL)
  2763.            strcpy (tpos - 1, tpos);
  2764.     }
  2765.  
  2766. /* Extract normal argument */
  2767.  
  2768.     else
  2769.     {
  2770.         spos = SkipToWhiteSpace (cpos);
  2771.         fn = _Ex_GetSpace (spos - cpos, cpos);
  2772.     }
  2773.  
  2774. /* Process argument */
  2775.  
  2776.     if (*cpos != CHAR_SINGLE_QUOTE)
  2777.         fn = _Ex_ConvertEnvVariables (fn);
  2778.  
  2779.     switch (*cpos)
  2780.     {
  2781.         case CHAR_INDIRECT:        /* Expand file             */
  2782.         _Ex_ExpandIndirectFile (fn, Alist);
  2783.         break;
  2784.  
  2785.         case CHAR_DOUBLE_QUOTE:    /* Expand string            */
  2786.         case CHAR_SINGLE_QUOTE:
  2787.         _Ex_SaveArgvValue (fn, FALSE, Alist);
  2788.         break;
  2789.  
  2790.         default:        /* Expand field                    */
  2791.         _Ex_ExpandField (fn, Alist);
  2792.     }
  2793.  
  2794.     ReleaseMemoryCell (fn);
  2795.     }
  2796. }
  2797. #endif
  2798.  
  2799. /*
  2800.  * Expand an indirect file Argument
  2801.  */
  2802.  
  2803. #if (OS_TYPE != OS_UNIX)
  2804. static void F_LOCAL _Ex_ExpandIndirectFile (char *file, Word_B **Alist)
  2805. {
  2806.     FILE        *fp;        /* File descriptor                */
  2807.     char    *EoLFound;    /* Pointer                */
  2808.     int        c_maxlen = MAX_LINE;
  2809.     char    *line;        /* Line buffer                    */
  2810.     char    *eolp;
  2811.  
  2812. /* If file open fails, expand as a field */
  2813.  
  2814.     if ((fp = fopen (file + 1, sOpenReadMode)) == (FILE *)NULL)
  2815.     PrintErrorMessage ("Cannot open re-direct file - %s (%s)\n",
  2816.                file + 1, strerror (errno));
  2817.  
  2818. /* Grab some memory for the line */
  2819.  
  2820.     line = (char *)GetAllocatedSpace (c_maxlen);
  2821.  
  2822. /* For each line in the file, remove EOF characters and add argument */
  2823.  
  2824.     while (fgets (line, c_maxlen, fp) != (char *)NULL)
  2825.     {
  2826.     EoLFound = strchr (line, CHAR_NEW_LINE);
  2827.     eolp = line;
  2828.  
  2829. /* Handle continuation characters */
  2830.  
  2831.     while (TRUE)
  2832.     {
  2833.  
  2834. /* Check for a continuation character */
  2835.  
  2836.         if (((EoLFound = strchr (eolp, CHAR_NEW_LINE)) != (char *)NULL) &&
  2837.         (*(EoLFound - 1) == CHAR_META))
  2838.         {
  2839.         *(EoLFound - 1) = CHAR_NEW_LINE;
  2840.         *EoLFound = 0;
  2841.         EoLFound = (char *)NULL;
  2842.         }
  2843.  
  2844.         else if (EoLFound == (char *)NULL)
  2845.         EoLFound = strchr (line, 0x1a);
  2846.  
  2847.         if (EoLFound != (char *)NULL)
  2848.         break;
  2849.  
  2850. /* Find the end of the line */
  2851.  
  2852.         c_maxlen = strlen (line);
  2853.  
  2854. /* Get some more space */
  2855.  
  2856.         line = (char *)ReAllocateSpace (line, c_maxlen + MAX_LINE);
  2857.         eolp = &line[c_maxlen];
  2858.  
  2859.         if (fgets (eolp, MAX_LINE, fp) == (char *)NULL)
  2860.         break;
  2861.     }
  2862.  
  2863. /* Terminate the line and add it to the argument list */
  2864.  
  2865.     if (EoLFound != (char *)NULL)
  2866.         *EoLFound = 0;
  2867.  
  2868.     _Ex_SaveArgvValue (line, FALSE, Alist);
  2869.     }
  2870.  
  2871.     if (ferror(fp))
  2872.     PrintErrorMessage ("%s (%s)", file + 1, strerror (errno));
  2873.  
  2874.     ReleaseMemoryCell (line);
  2875.     fclose (fp);
  2876.  
  2877. /* Delete tempoary files */
  2878.  
  2879.     if (((line = strrchr (file + 1, CHAR_PERIOD)) != (char *)NULL) &&
  2880.     (stricmp (line, ".tmp") == 0))
  2881.     unlink (file + 1);            /* Delete it        */
  2882. }
  2883. #endif
  2884.  
  2885. /*
  2886.  * Get space for an argument name
  2887.  */
  2888.  
  2889. #if (OS_TYPE != OS_UNIX)
  2890. static char * F_LOCAL _Ex_GetSpace (int length, char *in_s)
  2891. {
  2892.     char    *out_s;            /* Malloced space address       */
  2893.  
  2894. /* Copy string for specified length */
  2895.  
  2896.     out_s = strncpy ((char *)GetAllocatedSpace (length + 1), in_s, length);
  2897.     out_s[length] = 0;
  2898.     return (out_s);
  2899. }
  2900. #endif
  2901.  
  2902. /*
  2903.  * Skip over spaces
  2904.  */
  2905.  
  2906. static char * F_LOCAL _Ex_SkipWhiteSpace (char *a)
  2907. {
  2908.     while (isspace (*a))
  2909.         a++;
  2910.  
  2911.     return a;
  2912. }
  2913.  
  2914. /*
  2915.  * Skip over spaces
  2916.  */
  2917.  
  2918. char    *SkipToWhiteSpace (char *a)
  2919. {
  2920.     while (*a && !isspace (*a))
  2921.         a++;
  2922.  
  2923.     return a;
  2924. }
  2925.  
  2926. /* Find the location of meta-characters.  If no meta, add the argument and
  2927.  * return NULL.  If meta characters, return position of end of directory
  2928.  * name.  If not multiple directories, return -1
  2929.  */
  2930.  
  2931. #if (OS_TYPE != OS_UNIX)
  2932. static void F_LOCAL _Ex_ExpandField (char *file, Word_B **Alist)
  2933. {
  2934.     int        i;
  2935.     int        j;
  2936.     char    *vecp[2];
  2937.     char    **FileList;
  2938.  
  2939.     if (strpbrk (file, "?*[]\\") == (char *)NULL)
  2940.     {
  2941.     _Ex_SaveArgvValue (file, FALSE, Alist);
  2942.     return;
  2943.     }
  2944.  
  2945.     vecp[0] = file;
  2946.     vecp[1] = (char *)NULL;
  2947.  
  2948.     FileList = ExpandWordList (vecp, EXPAND_GLOBBING | EXPAND_TILDE,
  2949.                    (ExeMode *)NULL);
  2950.  
  2951.     j = CountNumberArguments (FileList);
  2952.  
  2953.     for (i = 0; i < j; )
  2954.     _Ex_SaveArgvValue (FileList[i++], FALSE, Alist);
  2955.  
  2956.     ReleaseAList (FileList);
  2957. }
  2958. #endif
  2959.  
  2960. /*
  2961.  * Process Environment - note that field is a malloc'ed field
  2962.  */
  2963.  
  2964. #if (OS_TYPE != OS_UNIX)
  2965. static char * F_LOCAL _Ex_ConvertEnvVariables (char *field)
  2966. {
  2967.     char    *sp, *cp, *np, *ep;
  2968.     char    save;
  2969.     int        b_flag;
  2970.  
  2971.     sp = field;
  2972.  
  2973. /* Replace any $ strings */
  2974.  
  2975.     while ((sp = strchr (sp, CHAR_VARIABLE)) != (char *)NULL)
  2976.     {
  2977.  
  2978. /* If ${...}, find the terminating } */
  2979.  
  2980.     if (*(cp = ++sp) == CHAR_OPEN_BRACES)
  2981.     {
  2982.         b_flag = 1;
  2983.         ++cp;
  2984.  
  2985.         while (*cp && (*cp != CHAR_CLOSE_BRACES))
  2986.         cp++;
  2987.     }
  2988.  
  2989. /* Else must be $..., find the terminating non-alphanumeric */
  2990.  
  2991.     else
  2992.     {
  2993.         b_flag = 0;
  2994.  
  2995.         while (isalnum (*cp))
  2996.         cp++;
  2997.     }
  2998.  
  2999. /* Grab the environment variable */
  3000.  
  3001.     if (cp == sp)
  3002.         continue;
  3003.  
  3004. /* Get its value */
  3005.  
  3006.     save = *cp;
  3007.     *cp = 0;
  3008.     ep = getenv (sp + b_flag);
  3009.     *cp = save;
  3010.  
  3011.     if (ep != (char *)NULL)
  3012.     {
  3013.         np = _Ex_GetSpace (strlen(field) - (cp - sp) + strlen (ep) - 1,
  3014.                        field);
  3015.         strcpy (&np[sp - field - 1], ep);
  3016.         ReleaseMemoryCell (field);
  3017.         strcpy ((sp = &np[strlen(np)]), cp + b_flag);
  3018.         field = np;
  3019.     }
  3020.     }
  3021.  
  3022.     return field;
  3023. }
  3024. #endif
  3025.  
  3026. /*
  3027.  * Handle EMX style arguments
  3028.  */
  3029.  
  3030. #if (OS_TYPE == OS_OS2)
  3031. static void F_LOCAL  _Ex_ProcessEMXArguments (char *argvp, Word_B **Alist)
  3032. {
  3033.     char    *cp;
  3034.     char    *s = argvp;
  3035.  
  3036.     _Ex_SaveArgvValue (argvp, TRUE, Alist);
  3037.  
  3038.     argvp += strlen (argvp) + 1;
  3039.     _ACmdLine = argvp;
  3040.  
  3041. /*
  3042.  * Add support in OS2 version for Eberhard Mattes EMX interface to commands.
  3043.  */
  3044.  
  3045.     if ((*argvp) && (*(cp = argvp + strlen (argvp) + 1) == CHAR_TILDE) &&
  3046.     (strcmp (s, PATH_TO_UNIX (cp + 1)) == 0))
  3047.     {
  3048.  
  3049. /* Skip over the program name at string 2 to the start of the first
  3050.  * argument at string 3
  3051.  */
  3052.  
  3053.     EMXStyleParameters = TRUE;
  3054.     argvp += strlen (argvp) + 1;
  3055.     argvp += strlen (argvp) + 1;
  3056.  
  3057.     while (*argvp)
  3058.     {
  3059.         cp = (*argvp == CHAR_TILDE) ? argvp + 1 : argvp;
  3060.  
  3061.         if (*cp == CHAR_INDIRECT)
  3062.         _Ex_ExpandIndirectFile (cp, Alist);
  3063.  
  3064.         else
  3065.         _Ex_SaveArgvValue (cp, FALSE, Alist);
  3066.  
  3067.         argvp += strlen (argvp) + 1;
  3068.     }
  3069.     }
  3070.  
  3071.     else
  3072.     _Ex_CommandLine (argvp, Alist);
  3073. }
  3074. #endif
  3075.  
  3076. /*
  3077.  * Save the next argument in the word block
  3078.  */
  3079.  
  3080. #if (OS_TYPE != OS_UNIX)
  3081. static void F_LOCAL _Ex_SaveArgvValue (char *value, bool Convert,
  3082.                        Word_B **Alist)
  3083. {
  3084.     if (Convert)
  3085.     PATH_TO_UNIX (value);
  3086.  
  3087.     *Alist = AddWordToBlock (value == (char *)NULL ? value : StringSave (value),
  3088.                      *Alist);
  3089.  
  3090.     if (value == (char *)NULL)
  3091.     {
  3092.     ARG_ARRAY = GetWordList (*Alist);
  3093.     ARG_COUNT = CountNumberArguments (ARG_ARRAY);
  3094.     }
  3095. }
  3096. #endif
  3097.  
  3098.  
  3099. #if (OS_TYPE == OS_DOS) && (OS_SIZE == OS_32) && !defined (__EMX__)
  3100. /*
  3101.  * INTERRUPT 24 - ERROR HANDLER - Output message
  3102.  *
  3103.  * doserror    - Error code
  3104.  * devhdr    - Device header address.
  3105.  * deverror    -
  3106.  *
  3107.  *   Bits     Meaning
  3108.  *
  3109.  *   15        Disk error if false (0).
  3110.  *        Other device error if true (1).
  3111.  *   14        Not used.
  3112.  *   13        "Ignore" response not allowed if false.
  3113.  *   12        "Retry" response not allowed if false.
  3114.  *   11        "Fail" response not allowed if false. (Note that DOS changes
  3115.  *        "fail" to "abort".)
  3116.  *   9-10    Location (for disk error)
  3117.  *
  3118.  *        0 - DOS
  3119.  *        1 - File Allocation Table (FAT)
  3120.  *        2 - Directory
  3121.  *        3 - Data area
  3122.  *
  3123.  *     8    Read error if false; write error if true
  3124.  *     0 - 7    Device Number
  3125.  */
  3126.  
  3127.  
  3128. char    *I24_Errors [] = {
  3129.     "Write-protect error",
  3130.     "Unknown unit",
  3131.     "Drive not ready",
  3132.     "Unknown command",
  3133.     "CRC error",
  3134.     "Bad request structure length",
  3135.     "Seek error",
  3136.     "Unknown media type",
  3137.     "Sector not found",
  3138.     "Out of paper",
  3139.     "Write fault",
  3140.     "Read fault",
  3141.     "General failure",
  3142.     "Sharing violation",
  3143.     "Lock violation",
  3144.     "Invalid disk change",
  3145.     "FCB unavailable",
  3146.     "Sharing buffer overflow",
  3147.     "Unknown error"
  3148. };
  3149.  
  3150. #define I24_ERROR_LAST        (ARRAY_SIZE (I24_Errors) - 1)
  3151. #define I24_ERROR_DEFAULT    I24_ERROR_LAST
  3152.  
  3153. /* 
  3154.  * Error locus
  3155.  */
  3156.  
  3157. char    *I24_Locus [] = {
  3158.     "Unknown",
  3159.     "Unknown",
  3160.     "Block",
  3161.     "Network",
  3162.     "Serial",
  3163.     "Memory",
  3164. };
  3165.  
  3166. #define I24_LOCUS_LAST        (ARRAY_SIZE (I24_Locus) - 1)
  3167. #define I24_LOCUS_DEFAULT    0
  3168.  
  3169. /*
  3170.  * Action
  3171.  */
  3172.  
  3173. char    *I24_Action [] = {
  3174.     "None recommended",
  3175.     "Retry, then abort or ignore",
  3176.     "Retry with delay, then abort or ignore",
  3177.     "Correct information supplied",
  3178.     "Abort with cleanup",
  3179.     "Abort without cleanup",
  3180.     "Ignore",
  3181.     "Retry after correcting",
  3182. };
  3183.  
  3184. #define I24_ACTION_LAST        (ARRAY_SIZE (I24_Action) - 1)
  3185. #define I24_ACTION_DEFAULT    0
  3186.  
  3187. /*
  3188.  * Class
  3189.  */
  3190.  
  3191. char    *I24_Class [] = {
  3192.     "Unknown error",
  3193.     "Out of resource",
  3194.     "Temporary failure",
  3195.     "Authorisation error",
  3196.     "MSDOS Internal error",
  3197.     "Hardware error",
  3198.     "System error",
  3199.     "Application error",
  3200.     "Item missing",
  3201.     "Item invalid",
  3202.     "Item interlocked",
  3203.     "Media problem"
  3204. };
  3205.  
  3206. #define I24_CLASS_LAST        (ARRAY_SIZE (I24_Class) - 1)
  3207. #define I24_CLASS_DEFAULT    0
  3208.  
  3209. static char    *I24_Space = "\n\r          ";
  3210.  
  3211. int __far    HardErrorHandler (unsigned int deverror,
  3212.                   unsigned int doserror,
  3213.                   unsigned int *devhdr)
  3214. {
  3215.     char        DeviceName[10];
  3216.     static char        MessageBuffer[300];
  3217.     char        *mp;
  3218.     int            ch;
  3219.     struct DOSERROR    Ecodes;
  3220.  
  3221. /* If Ignore set, ignore */
  3222.  
  3223.     if (IgnoreHardErrors)
  3224.     _hardresume (_HARDERR_FAIL);
  3225.  
  3226. /* Initialise device name */
  3227.  
  3228.     memset (DeviceName, 0, 10);
  3229.  
  3230. /* Get extended error codes */
  3231.  
  3232.     dosexterr (&Ecodes);
  3233.  
  3234. /* Output on message */
  3235.  
  3236.     if (deverror & 0x8000)
  3237.     {
  3238.     memcpy (DeviceName, (((char *)devhdr) + 10), 8);
  3239.  
  3240.     if ((mp = strchr (DeviceName, CHAR_SPACE)) == (char *)NULL)
  3241.        mp = DeviceName + 8;
  3242.  
  3243.     strcpy (mp, ":");
  3244.     }
  3245.     
  3246.     else
  3247.     sprintf (DeviceName, "%c:", (deverror & 0x0ff) + 'A');
  3248.  
  3249.     sprintf (MessageBuffer, "\n\r%s when %s %s %s\n\r",
  3250.          (doserror > I24_ERROR_LAST) ? I24_Errors[I24_ERROR_DEFAULT]
  3251.                      : I24_Errors [doserror],
  3252.          (deverror & 0x0100) ? "writing" : "reading",
  3253.          (deverror & 0x8000) ? "device " : "disk",
  3254.          DeviceName);
  3255.  
  3256.     OutputBIOSString (MessageBuffer);
  3257.  
  3258.     sprintf (MessageBuffer, "[Extended Code  : 0x%.4x%sClass : %s%sAction: %s%sLocus : %s device]\n\r",
  3259.          Ecodes.exterror, I24_Space,
  3260.          (Ecodes.class > I24_CLASS_LAST) ? I24_Class[I24_CLASS_DEFAULT]
  3261.                          : I24_Class[Ecodes.class],
  3262.          I24_Space,
  3263.          (Ecodes.action > I24_ACTION_LAST) ? I24_Action[I24_ACTION_DEFAULT]
  3264.                            : I24_Action[Ecodes.action],
  3265.          I24_Space,
  3266.          (Ecodes.locus > I24_LOCUS_LAST) ? I24_Locus[I24_LOCUS_DEFAULT]
  3267.                          : I24_Locus[Ecodes.locus]);
  3268.  
  3269.     OutputBIOSString (MessageBuffer);
  3270.  
  3271. /* Allowed actions */
  3272.  
  3273.     strcpy (MessageBuffer, "Abort");
  3274.     if (deverror & 0x2000)
  3275.     strcat (MessageBuffer, ", Ignore");
  3276.  
  3277.     if (deverror & 0x1000)
  3278.     strcat (MessageBuffer, ", Retry");
  3279.  
  3280.     if (deverror & 0x0800)
  3281.     strcat (MessageBuffer, ", Fail");
  3282.  
  3283.     OutputBIOSString (strcat (MessageBuffer, "? "));
  3284.  
  3285. /* Use BIOS to get a key. */
  3286.  
  3287.     while (TRUE)
  3288.     {
  3289.     ch = _bios_keybrd (_KEYBRD_READ) & 0x00ff;
  3290.  
  3291.     if ((ch = tolower (ch)) == 'a') 
  3292.     {
  3293.         OutputBIOSString ("A\n\r");
  3294.         _hardresume (_HARDERR_ABORT);
  3295.     }
  3296.  
  3297.     else if ((ch == 'i') && (deverror & 0x2000))
  3298.     {
  3299.         OutputBIOSString ("I\n\r");
  3300.         _hardresume (_HARDERR_IGNORE);
  3301.     }
  3302.  
  3303.     else if ((ch == 'r') && (deverror & 0x1000))
  3304.     {
  3305.         OutputBIOSString ("R\n\r");
  3306.         _hardresume (_HARDERR_RETRY);
  3307.     }
  3308.  
  3309.     else if ((ch == 'f') && (deverror & 0x0800))
  3310.     {
  3311.         OutputBIOSString ("F\n\r");
  3312. #  if defined (__WATCOMC__)
  3313.         _hardresume (_HARDERR_FAIL);
  3314. #  else
  3315.         _hardretn (doserror);
  3316. #  endif
  3317.     }
  3318.  
  3319.     OutputBIOSString ("\007");
  3320.     }
  3321.  
  3322. #  if defined (__WATCOMC__)
  3323.     return _HARDERR_FAIL;
  3324. #  endif
  3325. }
  3326.  
  3327. /*
  3328.  * Display a string using BIOS interrupt 0x0e (Write TTY).
  3329.  */
  3330.  
  3331. static void F_LOCAL OutputBIOSString (char *p)
  3332. {
  3333.     union REGS        r;
  3334.  
  3335.     while (*p)
  3336.     {
  3337.     r.h.ah = 0x0e;
  3338.         r.h.al = *(p++);
  3339.     SystemInterrupt (0x10, &r, &r);
  3340.     }
  3341. }
  3342. #endif
  3343.  
  3344. /*
  3345.  * Output to Stderr
  3346.  */
  3347.  
  3348. int    feputc (int c)
  3349. {
  3350.     return putc (c, stderr);
  3351. }
  3352.  
  3353. int    feputs (char *s)
  3354. {
  3355.     return fputs (s, stderr);
  3356. }
  3357.  
  3358. int    foputs (char *s)
  3359. {
  3360.     return fputs (s, stdout);
  3361. }
  3362.  
  3363. /*
  3364.  * Convert drives
  3365.  */
  3366.  
  3367. #if (OS_TYPE != OS_UNIX)
  3368. unsigned int    GetDriveNumber (char letter)
  3369. {
  3370.     return tolower (letter) - 'a' + 1;
  3371. }
  3372.  
  3373. char         GetDriveLetter (unsigned int drive)
  3374. {
  3375.     return (char)(drive + 'a' - 1);
  3376. }
  3377. #endif
  3378.  
  3379. /*
  3380.  * IO Map functions - Set or clear the inuse bit in the environment.
  3381.  */
  3382.  
  3383. void    ChangeFileDescriptorStatus (int fd, bool InUse)
  3384. {
  3385.     if (fd < FDBASE)
  3386.     return;
  3387.     
  3388.     else if (InUse)
  3389.     e.IOMap |= 1L << (fd - FDBASE);
  3390.     
  3391.     else
  3392.     e.IOMap &= ~(1L << (fd - FDBASE));
  3393. }
  3394.  
  3395. /*
  3396.  * Convert a string to a number
  3397.  */
  3398.  
  3399. int     GetNumericValue (char *as)
  3400. {
  3401.     long    value;
  3402.  
  3403.     return ConvertNumericValue (as, &value, 10) ? (int) value : -1;
  3404. }
  3405.  
  3406.  
  3407. /*
  3408.  * ProcessOutputMetaCharacters - Convert an escaped character to a binary value.
  3409.  *
  3410.  * Returns the binary value and updates the string pointer.
  3411.  */
  3412.  
  3413. static struct {
  3414.     char    Escaped;
  3415.     int        NewValue;
  3416. } ConvertMetaCharacters [] =
  3417. {
  3418.     { 'b',  0x08}, { 'f',  0x0c}, { 'v',    0x0b},        { 'n',  0x0a},
  3419.     { 'r',  0x0d}, { 't',  0x09}, { CHAR_META,  CHAR_META}, { 'c',  -1},
  3420.     { 0, 0}
  3421. };
  3422.  
  3423. int ProcessOutputMetaCharacters (char **cp)
  3424. {
  3425.     int        c_val = **cp;            /* Current character    */
  3426.     int        j = 0;
  3427.  
  3428.     if (c_val)
  3429.         (*cp)++;
  3430.  
  3431. /* Process escaped characters */
  3432.  
  3433.     while (ConvertMetaCharacters[j].Escaped != 0)
  3434.     {
  3435.      if (ConvertMetaCharacters[j].Escaped == (char)c_val)
  3436.         return ConvertMetaCharacters[j].NewValue;
  3437.  
  3438.     ++j;
  3439.     }
  3440.  
  3441. /* Check for an octal string */
  3442.  
  3443.     if (IS_OCTAL (c_val) && IS_OCTAL (**cp) && IS_OCTAL (*((*cp) + 1)))
  3444.     {
  3445.     c_val = ((c_val & 0x07) << 6) |
  3446.         ((**cp & 0x07) << 3)  |
  3447.         ((*((*cp) + 1) & 0x07));
  3448.  
  3449.     (*cp) += 2;
  3450.     return c_val;
  3451.     }
  3452.  
  3453.     return c_val;
  3454. }
  3455.  
  3456.  
  3457. /*
  3458.  * Extract the next path from a string and build a new path from the
  3459.  * extracted path and a file name
  3460.  */
  3461.  
  3462. char *BuildNextFullPathName (char *path_s,    /* Path string        */
  3463.                  char *file_s,    /* File name string    */
  3464.                  char *output_s)    /* Output path        */
  3465. {
  3466.     char    *s = output_s;
  3467.     int        fsize = 0;
  3468.  
  3469.     while (*path_s && (*path_s != CHAR_PATH_SEPARATOR) && (fsize++ < FFNAME_MAX))
  3470.     *s++ = *path_s++;
  3471.  
  3472.     if ((output_s != s) && !IsPathCharacter (*(s - 1)) && (fsize++ < FFNAME_MAX))
  3473.     *s++ = CHAR_UNIX_DIRECTORY;
  3474.  
  3475.     *s = 0;
  3476.  
  3477.     if (file_s != (char *)NULL)
  3478.     strncpy (s, file_s, FFNAME_MAX - fsize);
  3479.  
  3480.     output_s[FFNAME_MAX - 1] = 0;
  3481.  
  3482.     return (*path_s ? ++path_s : (char *)NULL);
  3483. }
  3484.  
  3485. /*
  3486.  * Go To the specified directory
  3487.  */
  3488.  
  3489. bool    GotoDirectory (char *CNDirectory, unsigned int cdrive)
  3490. {
  3491.     if (IsDriveCharacter (*(CNDirectory + 1)))
  3492.     {
  3493.     if (SetCurrentDrive (GetDriveNumber (*CNDirectory)) == -1)
  3494.         return FALSE;
  3495.  
  3496.     CNDirectory += 2;
  3497.     }
  3498.  
  3499. /* Was the change successful? */
  3500.  
  3501.     if ((!*CNDirectory) || (S_chdir (CNDirectory)))
  3502.     return TRUE;
  3503.  
  3504.     SetCurrentDrive (cdrive);
  3505.     return FALSE;
  3506. }
  3507.  
  3508. /*
  3509.  * Find out the application type
  3510.  */
  3511.  
  3512. unsigned long    QueryApplicationType (const char *pathname)
  3513. {
  3514. #if (OS_TYPE == OS_UNIX)
  3515.     return EXETYPE_UNIX_NATIVE;
  3516. #else
  3517.     int            fd;
  3518.     unsigned long    res;
  3519.     size_t        len;
  3520.  
  3521.     if (pathname == NULL)
  3522.     return EXETYPE_BAD_FILE;
  3523.     
  3524. /* Open the file */
  3525.  
  3526.     if ((fd = open (pathname, O_RDONLY | O_BINARY)) == -1)
  3527.     return EXETYPE_BAD_FILE;
  3528.  
  3529.     res = QueryApplicationType1 (fd);
  3530.     close (fd);
  3531.  
  3532. /* Check for .com file */
  3533.  
  3534.     if ((res == EXETYPE_UNKNOWN) && ((len = strlen (pathname)) > 5) &&
  3535.         (stricmp (&pathname[len - 4], ".com") == 0))
  3536.     return EXETYPE_DOS_CUI;
  3537.  
  3538.     return res;
  3539. #endif
  3540. }
  3541.  
  3542. /*
  3543.  * Do the actual work!  Under UNIX, we don't care
  3544.  */
  3545.  
  3546. #if (OS_TYPE != OS_UNIX)
  3547. static unsigned long F_LOCAL QueryApplicationType1 (int fd)
  3548. {
  3549.     union {
  3550.     struct ExecOS2_16Header    OS2aHead;
  3551.     struct ExecOS2_32header    OS2bHead;
  3552.     struct ExecNTHeader    NTHead;
  3553.     struct ExecDosHeader    DosHead;
  3554.     }                OS_Headers;
  3555.     struct stat            fstatus;
  3556.  
  3557.     if ((read (fd, &OS_Headers, sizeof (struct ExecDosHeader)) !=
  3558.         sizeof (struct ExecDosHeader)) ||
  3559.         (OS_Headers.DosHead.e_magic != SIG_DOS))
  3560.     return EXETYPE_UNKNOWN;
  3561.     
  3562. /*
  3563.  * If the header size in the header is not a new header or the relocation
  3564.  * section starts before the end of the new header, it must be a DOS program
  3565.  */
  3566.  
  3567.     if ((OS_Headers.DosHead.e_cparhdr * 16 < sizeof (struct ExecDosHeader)) ||
  3568.     (OS_Headers.DosHead.e_lfarlc < sizeof (struct ExecDosHeader)))
  3569.         return EXETYPE_DOS_CUI;
  3570.     
  3571.     if ((fstat (fd, &fstatus) == -1) ||
  3572.     (fstatus.st_size < (off_t) OS_Headers.DosHead.e_lfanew))
  3573.         return EXETYPE_DOS_CUI;
  3574.  
  3575.     if ((lseek (fd, (off_t) OS_Headers.DosHead.e_lfanew, SEEK_SET) ==
  3576.         (off_t) -1) ||
  3577.     (read (fd, &OS_Headers, sizeof (OS_Headers)) != sizeof (OS_Headers)))
  3578.     return EXETYPE_BAD_IMAGE;
  3579.     
  3580. /*
  3581.  * Check for NT
  3582.  */
  3583.  
  3584.     if (OS_Headers.NTHead.Signature == SIG_NT)
  3585.     {
  3586.     if (OS_Headers.NTHead.FileHeader.SizeOfOptionalHeader !=
  3587.         NT_OPTIONAL_HEADER)
  3588.         return EXETYPE_UNKNOWN;
  3589.  
  3590.     switch (OS_Headers.NTHead.OptionalHeader.Subsystem)
  3591.     {
  3592.         default:
  3593.         return EXETYPE_UNKNOWN;
  3594.  
  3595.         case NT_SS_NATIVE:
  3596.         return EXETYPE_NT_NATIVE;
  3597.  
  3598.         case NT_SS_WINDOWS_GUI:
  3599.         return EXETYPE_NT_WINDOWS_GUI;
  3600.  
  3601.         case NT_SS_WINDOWS_CUI:
  3602.         return EXETYPE_NT_WINDOWS_CUI;
  3603.  
  3604.         case NT_SS_OS2_CUI:
  3605.         return EXETYPE_NT_OS2;
  3606.  
  3607.         case NT_SS_POSIX_CUI:
  3608.         return EXETYPE_NT_POSIX;
  3609.     }
  3610.     }
  3611.  
  3612. /* OS2 1.x */
  3613.  
  3614.     else if ((OS_Headers.OS2aHead.ne_magic == SIG_OS2_16) ||
  3615.          (OS_Headers.OS2aHead.ne_magic == SIG_OS2_16LE))
  3616.     {
  3617. #ifdef APPDEBUG
  3618.         fprintf (stderr, "ne_flags       = %.4x  ne_flagsothers = %.4x\n",
  3619.          OS_Headers.OS2aHead.ne_flags,
  3620.          OS_Headers.OS2aHead.ne_flagsothers);
  3621.         fprintf (stderr, "ne_exetyp      = %.4x (R %.2x V %.2x)\n",
  3622.          OS_Headers.OS2aHead.ne_exetyp,
  3623.          OS_Headers.OS2aHead.ne_ver,
  3624.          OS_Headers.OS2aHead.ne_rev);
  3625. #endif
  3626.  
  3627.     if (OS_Headers.OS2aHead.ne_flags & (OS2_16_NOTP | OS2_16_IERR))
  3628.         return EXETYPE_BAD_IMAGE;
  3629.  
  3630.     if (OS_Headers.OS2aHead.ne_exetyp == OS2_16_WINDOWS)
  3631.         return EXETYPE_DOS_GUI;
  3632.  
  3633. /* This appears to be what Watcom generates */
  3634.  
  3635.     else if ((OS_Headers.OS2aHead.ne_exetyp == OS2_16_UNKNOWN) &&
  3636.          (OS_Headers.OS2aHead.ne_flags == 0))
  3637.         return EXETYPE_DOS_32;
  3638.  
  3639.  
  3640. /* Under OS/2, A bound app is an OS/2 app otherwise its a DOS app */
  3641.  
  3642. #if (OS_TYPE != OS_OS2)
  3643.     else if (OS_Headers.OS2aHead.ne_exetyp == OS2_16_UNKNOWN)
  3644.         return EXETYPE_DOS_CUI;
  3645.  
  3646.     else if (OS_Headers.OS2aHead.ne_flags & OS2_16_BOUND)
  3647.         return EXETYPE_DOS_BOUND;
  3648. #else
  3649.     else if (OS_Headers.OS2aHead.ne_exetyp == OS2_16_UNKNOWN)
  3650.         return EXETYPE_OS2_CUI;
  3651. #endif
  3652.  
  3653. /* Real OS/2 app */
  3654.  
  3655.     else if (OS_Headers.OS2aHead.ne_exetyp == OS2_16_OS2)
  3656.     {
  3657.         switch (OS_Headers.OS2aHead.ne_flags & OS2_16_APPTYP)
  3658.         {
  3659.             case OS2_16_NOTWINCOMPAT:
  3660.             return EXETYPE_OS2_CUI;
  3661.  
  3662.         case OS2_16_WINCOMPAT:
  3663.             return EXETYPE_OS2_CGUI;
  3664.  
  3665.         case OS2_16_WINAPI:
  3666.             return EXETYPE_OS2_GUI;
  3667.  
  3668.         case 0:
  3669. #if (OS_TYPE == OS_OS2)
  3670.             return EXETYPE_OS2_CUI;
  3671. #else
  3672.             return EXETYPE_DOS_BOUND;
  3673. #endif
  3674.         }
  3675.     }
  3676.     }
  3677.  
  3678. /* OS2 2.x */
  3679.  
  3680.     else if (OS_Headers.OS2bHead.e32_magic == SIG_OS2_32)
  3681.     {
  3682. #ifdef APPDEBUG
  3683.         fprintf (stderr, "Mflags = %.8lx\n", OS_Headers.OS2bHead.e32_mflags);
  3684. #endif
  3685.  
  3686.         if ((OS_Headers.OS2bHead.e32_mflags & (OS2_NOTP | OS2_NOLOAD)) ||
  3687.         (OS_Headers.OS2bHead.e32_mflags & OS2_MODMASK))
  3688.         return EXETYPE_NOT_EXE;
  3689.     
  3690.     if ((OS_Headers.OS2bHead.e32_mflags & OS2_APPMASK) == OS2_NOPMW)
  3691.         return EXETYPE_OS2_CUI | EXETYPE_OS2_32;
  3692.  
  3693.     else if ((OS_Headers.OS2bHead.e32_mflags & OS2_APPMASK) == OS2_PMW)
  3694.         return EXETYPE_OS2_CGUI | EXETYPE_OS2_32;
  3695.  
  3696.     else if ((OS_Headers.OS2bHead.e32_mflags & OS2_APPMASK) == OS2_PMAPI)
  3697.         return EXETYPE_OS2_GUI | EXETYPE_OS2_32;
  3698.     }
  3699.  
  3700. /* Give UP! */
  3701.  
  3702.     return EXETYPE_UNKNOWN;
  3703. }
  3704. #endif
  3705.  
  3706. /*
  3707.  * Need case change for UNIX
  3708.  */
  3709.  
  3710. #if (OS_TYPE == OS_UNIX)
  3711.  
  3712. /* Convert a string to lower case */
  3713.  
  3714. char    *strlwr (char *s)
  3715. {
  3716.     char    *original = s;
  3717.  
  3718.     if (s != (char *)NULL)
  3719.     {
  3720.     while (*s)
  3721.     {
  3722.         *s = tolower (*s);
  3723.         s++;
  3724.     }
  3725.     }
  3726.  
  3727.     return (original);
  3728. }
  3729.  
  3730. /*
  3731.  * Convert a string to upper case
  3732.  */
  3733.  
  3734. char    *strupr (char *s)
  3735. {
  3736.     char    *original = s;
  3737.  
  3738.     if (s != (char *)NULL)
  3739.     {
  3740.     while (*s)
  3741.     {
  3742.         *s = toupper (*s);
  3743.         s++;
  3744.     }
  3745.     }
  3746.  
  3747.     return (original);
  3748. }
  3749.  
  3750. /*
  3751.  * String compare - ignore case
  3752.  */
  3753.  
  3754. int    stricmp (char *a, char *b)
  3755. {
  3756.     int        diff;
  3757.  
  3758.     while ((!(diff = tolower(*a) - toupper (*b))) && *a)
  3759.     {
  3760.     a++;
  3761.     b++;
  3762.     }
  3763.  
  3764.     return diff;
  3765. }
  3766. #endif
  3767.  
  3768. /*
  3769.  * Convert long to based number string
  3770.  */
  3771.  
  3772. #if (OS_TYPE == OS_UNIX) || defined (__EMX__)
  3773. char    *ltoa (long n, char *ibuffer, int base)
  3774. {
  3775.     char        flag = 0;
  3776.     int            r;
  3777.     char        lbuffer[32 + 2];
  3778.     char        *c_pos = lbuffer + 32 + 2;
  3779.  
  3780.     if ((base < 2) || (base > 36))
  3781.     {
  3782.     errno = ERANGE;
  3783.     return (char *)NULL;
  3784.     }
  3785.  
  3786.     *(--c_pos) = 0;
  3787.  
  3788.     if (n < 0)
  3789.     {
  3790.     flag++;
  3791.     n = -n;
  3792.     }
  3793.  
  3794.     if (n == 0)
  3795.     *(--c_pos) = '0';
  3796.     
  3797.     else
  3798.     {
  3799.     while (n > 0)
  3800.     {
  3801.         r = (int)(n % (long)base);
  3802.  
  3803.         *(--c_pos) = r + ((r > 9) ? 'a' - 10 : '0');
  3804.  
  3805.         n /= (long)base;
  3806.     }
  3807.     }
  3808.  
  3809.     if (flag)
  3810.     *(--c_pos) = (char)'-';
  3811.  
  3812.     return strcpy (ibuffer, c_pos);
  3813. }
  3814. #endif
  3815.  
  3816. /*
  3817.  * EMX does not have a cwait function
  3818.  */
  3819.  
  3820. #if defined (__EMX__) && (OS_TYPE == OS_OS2)
  3821. int    cwait (int *TermCode, int pid, int action)
  3822. {
  3823.     ULONG        rc;
  3824.     RESULTCODES        res;
  3825.     PID            rpid;
  3826.     PID            apid = pid;
  3827.  
  3828.     if ((rc = DosWaitChild (action == WAIT_GRANDCHILD ? DCWA_PROCESSTREE
  3829.                                   : DCWA_PROCESS,
  3830.                 DCWW_WAIT, &res, &rpid, apid)))
  3831.     {
  3832.     if (rc == ERROR_INVALID_PROCID)
  3833.         errno = EINVAL;
  3834.  
  3835.     else
  3836.          errno = ECHILD;
  3837.     
  3838.     return (-1);
  3839.     }
  3840.  
  3841.     *TermCode = res.codeResult;
  3842.     return rpid;
  3843. }
  3844. #endif
  3845.  
  3846. /*
  3847.  * DEBUG code
  3848.  */
  3849.  
  3850. #ifdef DEBUG_ON
  3851. void    db_printf (char *fmt, ...)
  3852. {
  3853.     va_list    ap;
  3854.  
  3855.     va_start (ap, fmt);
  3856.     vfprintf (stderr, fmt, ap);
  3857.     fputc (CHAR_NEW_LINE, stderr);
  3858.     fflush (stderr);
  3859.     va_end (ap);
  3860. }
  3861. #endif
  3862.  
  3863. /*
  3864.  * Get file attributes - EMX version
  3865.  */
  3866.  
  3867. #if (OS_TYPE == OS_DOS) && defined (__EMX__)
  3868. unsigned    _dos_getfileattr (char *file, unsigned int *attr)
  3869. {
  3870.     struct _find    dtabuf;
  3871.     int            rc;
  3872.  
  3873.     DISABLE_HARD_ERRORS;
  3874.  
  3875.     rc = __findfirst (file, OS_FILE_ATTRIBUTES, &dtabuf);
  3876.  
  3877.     ENABLE_HARD_ERRORS;
  3878.  
  3879.     *attr = (char)dtabuf.attr;
  3880.  
  3881.     return (unsigned int)rc;
  3882. }
  3883. #endif
  3884.